MM-67957 H2-H9 Enzyme to RTL bulk migration (#35670)

* (test): h2-9 enzyme to rtl bulk migration

* update per comments and fix types

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
sabril
2026-03-30 09:59:44 +08:00
committed by GitHub
co-authored by Mattermost Build
parent fd2dd1c618
commit 36bc59c1b7
56 changed files with 26752 additions and 14029 deletions
@@ -1,18 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {Channel} from '@mattermost/types/channels';
import ChannelList from 'components/admin_console/data_retention_settings/channel_list/channel_list';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
describe('components/admin_console/data_retention_settings/channel_list', () => {
const channel: Channel = Object.assign(TestHelper.getChannelMock({id: 'channel-1'}));
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
(console.error as jest.Mock).mockClear();
});
test('should match snapshot', () => {
const testChannels = [{
...channel,
@@ -28,7 +35,7 @@ describe('components/admin_console/data_retention_settings/channel_list', () =>
setChannelListSearch: jest.fn(),
setChannelListFilters: jest.fn(),
};
const wrapper = shallow(
const {container} = renderWithContext(
<ChannelList
searchTerm=''
filters={{}}
@@ -38,8 +45,9 @@ describe('components/admin_console/data_retention_settings/channel_list', () =>
channels={testChannels}
totalCount={testChannels.length}
actions={actions}
/>);
expect(wrapper).toMatchSnapshot();
/>,
);
expect(container).toMatchSnapshot();
});
test('should match snapshot with paging', () => {
@@ -61,7 +69,7 @@ describe('components/admin_console/data_retention_settings/channel_list', () =>
setChannelListFilters: jest.fn(),
};
const wrapper = shallow(
const {container} = renderWithContext(
<ChannelList
searchTerm=''
filters={{}}
@@ -71,8 +79,13 @@ describe('components/admin_console/data_retention_settings/channel_list', () =>
channels={testChannels}
totalCount={30}
actions={actions}
/>);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
/>,
);
// With 30 channels and page size 10, should show first 10 and pagination
expect(screen.getByText('DN0')).toBeInTheDocument();
expect(screen.getByText('DN9')).toBeInTheDocument();
expect(screen.getByText((content) => content.includes('1') && content.includes('10') && content.includes('30'))).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import CustomPolicyForm from 'components/admin_console/data_retention_settings/custom_policy_form/custom_policy_form';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('components/admin_console/data_retention_settings/custom_policy_form', () => {
const defaultProps = {
actions: {
@@ -21,16 +22,32 @@ describe('components/admin_console/data_retention_settings/custom_policy_form',
},
};
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
(console.error as jest.Mock).mockClear();
});
test('should match snapshot with creating new policy', () => {
const props = {...defaultProps};
const wrapper = shallow(<CustomPolicyForm {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(
<CustomPolicyForm {...props}/>,
{},
{useMockedStore: true},
);
expect(screen.getByText('Custom Retention Policy')).toBeInTheDocument();
expect(screen.getByText('Name and retention')).toBeInTheDocument();
expect(screen.getByText('Assigned teams')).toBeInTheDocument();
expect(screen.getByText('Assigned channels')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
test('should match snapshot with editing existing policy', () => {
const props = {...defaultProps};
const wrapper = shallow(
const {container} = renderWithContext(
<CustomPolicyForm
{...props}
policyId='fsdgdsgdsgh'
@@ -42,7 +59,11 @@ describe('components/admin_console/data_retention_settings/custom_policy_form',
channel_count: 2,
}}
/>,
{},
{useMockedStore: true},
);
expect(wrapper).toMatchSnapshot();
expect(screen.getByText('Custom Retention Policy')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});
@@ -1,265 +1,260 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/PluginManagement should match snapshot 1`] = `
<div
className="wrapper--fixed DataRetentionSettings"
>
<AdminHeader
withBackButton={true}
>
<div>
<Connect(Component)
className="fa fa-angle-left back"
to="/admin_console/compliance/data_retention_settings"
/>
<MemoizedFormattedMessage
defaultMessage="Global Retention Policy"
id="admin.data_retention.globalPolicyTitle"
/>
</div>
</AdminHeader>
<div>
<div
className="admin-console__wrapper"
class="wrapper--fixed DataRetentionSettings"
>
<div
className="admin-console__content"
class="admin-console__header with-back"
>
<Card
className="console"
expanded={true}
<div>
<a
class="fa fa-angle-left back"
href="/admin_console/compliance/data_retention_settings"
/>
Global Retention Policy
</div>
</div>
<div
class="admin-console__wrapper"
>
<div
class="admin-console__content"
>
<CardBody>
<div
class="Card console expanded"
>
<div
className="global_policy"
class="Card__body expanded expanding"
style="height: 0px;"
>
<p>
<MemoizedFormattedMessage
defaultMessage="Applies to all teams and channels, but does not apply to custom retention policies."
id="admin.data_retention.form.text"
/>
</p>
<div
id="global_direct_message_dropdown"
class="global_policy"
>
<DropdownInputHybrid
defaultValue={
Object {
"label": <div>
<i
className="icon icon-infinity option-icon"
<p>
Applies to all teams and channels, but does not apply to custom retention policies.
</p>
<div
id="global_direct_message_dropdown"
>
<div
class="DropdownInput hybrid_container"
style="width: 100%;"
>
<fieldset
class="Input_fieldset Input_fieldset___legend Input_fieldset___split"
>
<legend
class="Input_legend Input_legend___focus"
>
Channel & direct message retention
</legend>
<div
class="Input_wrapper input_hybrid_wrapper showInput"
style="max-width: 10000px;"
>
<input
class="Input form-control"
id="channel_message_retention_input"
name="Input_channel_message_retention"
placeholder="Channel & direct message retention"
type="number"
value="100"
/>
<span
className="option_forever"
</div>
<div
class="Input_wrapper dropdown_hybrid_wrapper"
style="width: 90px;"
>
<div
class="Input Input__focus css-b62m3t-container"
id="DropdownInput_channel_message_retention"
>
Keep forever
</span>
</div>,
"value": "FOREVER",
}
}
dropdownClassNamePrefix="channel_message_retention_dropdown"
exceptionToInput={
Array [
"FOREVER",
]
}
inputId="channel_message_retention_input"
inputType="number"
inputValue="100"
legend={
Object {
"defaultMessage": "Channel & direct message retention",
"id": "admin.data_retention.form.channelAndDirectMessageRetention",
}
}
name="channel_message_retention"
onDropdownChange={[Function]}
onInputChange={[Function]}
options={
Array [
Object {
"label": <span
className="option_hours"
>
Hours
</span>,
"value": "HOURS",
},
Object {
"label": <span
className="option_days"
>
Days
</span>,
"value": "DAYS",
},
Object {
"label": <span
className="option_years"
>
Years
</span>,
"value": "YEARS",
},
Object {
"label": <div>
<i
className="icon icon-infinity option-icon"
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
/>
<span
className="option_forever"
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="DropdownInput__controlContainer"
>
Keep forever
</span>
</div>,
"value": "FOREVER",
},
]
}
placeholder={
Object {
"defaultMessage": "Channel & direct message retention",
"id": "admin.data_retention.form.channelAndDirectMessageRetention",
}
}
value={
Object {
"label": <span
className="option_days"
<div
class="channel_message_retention_dropdown__control css-1bhmcib-control"
>
<div
class="channel_message_retention_dropdown__value-container channel_message_retention_dropdown__value-container--has-value css-hlgwow"
>
<div
class="channel_message_retention_dropdown__single-value css-1dimb5e-singleValue"
>
<span
class="option_days"
>
Days
</span>
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
aria-readonly="true"
class="css-1s80ejz-dummyInput-DummyInput"
id="react-select-2-input"
inputmode="none"
role="combobox"
tabindex="0"
value=""
/>
</div>
<div
class="DropdownInput__indicatorsContainer"
>
<div
class="channel_message_retention_dropdown__indicators css-1wy0on6"
>
<i
class="icon icon-chevron-down"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div
id="global_file_dropdown"
>
<div
class="DropdownInput hybrid_container"
style="width: 100%;"
>
<fieldset
class="Input_fieldset Input_fieldset___legend Input_fieldset___split"
>
<legend
class="Input_legend Input_legend___focus"
>
Days
</span>,
"value": "DAYS",
}
}
width={90}
/>
</div>
<div
id="global_file_dropdown"
>
<DropdownInputHybrid
defaultValue={
Object {
"label": <div>
<i
className="icon icon-infinity option-icon"
File retention
</legend>
<div
class="Input_wrapper input_hybrid_wrapper showInput"
style="max-width: 10000px;"
>
<input
class="Input form-control"
id="file_retention_input"
name="Input_file_retention"
placeholder="File retention"
type="number"
value="100"
/>
<span
className="option_forever"
</div>
<div
class="Input_wrapper dropdown_hybrid_wrapper"
style="width: 90px;"
>
<div
class="Input Input__focus css-b62m3t-container"
id="DropdownInput_file_retention"
>
Keep forever
</span>
</div>,
"value": "FOREVER",
}
}
dropdownClassNamePrefix="file_retention_dropdown"
exceptionToInput={
Array [
"FOREVER",
]
}
inputId="file_retention_input"
inputType="number"
inputValue="100"
legend={
Object {
"defaultMessage": "File retention",
"id": "admin.data_retention.form.fileRetention",
}
}
name="file_retention"
onDropdownChange={[Function]}
onInputChange={[Function]}
options={
Array [
Object {
"label": <span
className="option_hours"
>
Hours
</span>,
"value": "HOURS",
},
Object {
"label": <span
className="option_days"
>
Days
</span>,
"value": "DAYS",
},
Object {
"label": <span
className="option_years"
>
Years
</span>,
"value": "YEARS",
},
Object {
"label": <div>
<i
className="icon icon-infinity option-icon"
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
/>
<span
className="option_forever"
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="DropdownInput__controlContainer"
>
Keep forever
</span>
</div>,
"value": "FOREVER",
},
]
}
placeholder={
Object {
"defaultMessage": "File retention",
"id": "admin.data_retention.form.fileRetention",
}
}
value={
Object {
"label": <span
className="option_days"
>
Days
</span>,
"value": "DAYS",
}
}
width={90}
/>
<div
class="file_retention_dropdown__control css-1bhmcib-control"
>
<div
class="file_retention_dropdown__value-container file_retention_dropdown__value-container--has-value css-hlgwow"
>
<div
class="file_retention_dropdown__single-value css-1dimb5e-singleValue"
>
<span
class="option_days"
>
Days
</span>
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
aria-readonly="true"
class="css-1s80ejz-dummyInput-DummyInput"
id="react-select-3-input"
inputmode="none"
role="combobox"
tabindex="0"
value=""
/>
</div>
<div
class="DropdownInput__indicatorsContainer"
>
<div
class="file_retention_dropdown__indicators css-1wy0on6"
>
<i
class="icon icon-chevron-down"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</div>
</CardBody>
</Card>
</div>
</div>
</div>
</div>
<div
className="admin-console-save"
>
<SaveButton
defaultMessage={
<Memo(MemoizedFormattedMessage)
defaultMessage="Save"
id="admin.data_retention.custom_policy.save"
/>
}
disabled={true}
onClick={[Function]}
saving={false}
/>
<Connect(Component)
className="btn btn-tertiary"
to="/admin_console/compliance/data_retention_settings"
<div
class="admin-console-save"
>
<MemoizedFormattedMessage
defaultMessage="Cancel"
id="admin.data_retention.custom_policy.cancel"
/>
</Connect(Component)>
<button
class="btn btn-primary"
data-testid="saveSetting"
disabled=""
id="saveSetting"
type="submit"
>
<span>
Save
</span>
</button>
<a
class="btn btn-tertiary"
href="/admin_console/compliance/data_retention_settings"
>
Cancel
</a>
</div>
</div>
</div>
`;
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GlobalPolicyForm from 'components/admin_console/data_retention_settings/global_policy_form/global_policy_form';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('components/PluginManagement', () => {
const defaultProps = {
config: {
@@ -28,7 +29,10 @@ describe('components/PluginManagement', () => {
test('should match snapshot', () => {
const props = {...defaultProps};
const wrapper = shallow(<GlobalPolicyForm {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<GlobalPolicyForm {...props}/>);
expect(screen.getByText('Global Retention Policy')).toBeInTheDocument();
expect(screen.getByText('Applies to all teams and channels, but does not apply to custom retention policies.')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});
@@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {Team} from '@mattermost/types/teams';
import TeamList from 'components/admin_console/data_retention_settings/team_list/team_list';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
describe('components/admin_console/data_retention_settings/team_list', () => {
@@ -23,7 +23,7 @@ describe('components/admin_console/data_retention_settings/team_list', () => {
setTeamListSearch: jest.fn(),
setTeamListFilters: jest.fn(),
};
const wrapper = shallow(
const {container} = renderWithContext(
<TeamList
searchTerm=''
onRemoveCallback={jest.fn()}
@@ -32,8 +32,9 @@ describe('components/admin_console/data_retention_settings/team_list', () => {
teams={testTeams}
totalCount={testTeams.length}
actions={actions}
/>);
expect(wrapper).toMatchSnapshot();
/>,
);
expect(container).toMatchSnapshot();
});
test('should match snapshot with paging', () => {
@@ -52,7 +53,7 @@ describe('components/admin_console/data_retention_settings/team_list', () => {
setTeamListSearch: jest.fn(),
setTeamListFilters: jest.fn(),
};
const wrapper = shallow(
const {container} = renderWithContext(
<TeamList
searchTerm=''
onRemoveCallback={jest.fn()}
@@ -61,8 +62,13 @@ describe('components/admin_console/data_retention_settings/team_list', () => {
teams={testTeams}
totalCount={testTeams.length}
actions={actions}
/>);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
/>,
);
// With 30 teams and page size 10, should show first 10 and pagination
expect(screen.getByText('DN0')).toBeInTheDocument();
expect(screen.getByText('DN9')).toBeInTheDocument();
expect(screen.getByText((content) => content.includes('1') && content.includes('10') && content.includes('30'))).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});
@@ -1,314 +1,341 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on checked row 1`] = `
<div
className="group checked"
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group checked"
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check checked"
class="group-name"
>
<Memo(CheckboxCheckedIcon) />
</div>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
>
<a
className=""
href="#"
onClick={[Function]}
<div
class="group-check checked"
>
<i
className="icon fa fa-unlink"
/>
<MemoizedFormattedMessage
defaultMessage="Not Linked"
id="admin.group_settings.group_row.not_linked"
/>
</a>
</span>
<span
className="group-actions"
/>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span>
name
</span>
</div>
<div
class="group-content"
>
<span
class="group-description"
>
<a
class=""
href="#"
>
<i
class="icon fa fa-unlink"
/>
Not Linked
</a>
</span>
<span
class="group-actions"
/>
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on failed linked row 1`] = `
<div
className="group "
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group "
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check "
/>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
class="group-name"
>
<a
className="warning"
href="#"
onClick={[Function]}
>
<i
className="icon fa fa-exclamation-triangle"
/>
<MemoizedFormattedMessage
defaultMessage="Unlink failed"
id="admin.group_settings.group_row.unlink_failed"
/>
</a>
</span>
<span
className="group-actions"
<div
class="group-check "
/>
<span>
name
</span>
</div>
<div
class="group-content"
>
<Link
id="name_configure"
to="/admin_console/user_management/groups/group-id"
<span
class="group-description"
>
<MemoizedFormattedMessage
defaultMessage="Configure"
id="admin.group_settings.group_row.configure"
/>
</Link>
</span>
<a
class="warning"
href="#"
>
<i
class="icon fa fa-exclamation-triangle"
/>
Unlink failed
</a>
</span>
<span
class="group-actions"
>
<a
href="/admin_console/user_management/groups/group-id"
id="name_configure"
>
Configure
</a>
</span>
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on failed not linked row 1`] = `
<div
className="group "
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group "
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check "
/>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
class="group-name"
>
<a
className="warning"
href="#"
onClick={[Function]}
<div
class="group-check "
/>
<span>
name
</span>
</div>
<div
class="group-content"
>
<span
class="group-description"
>
<i
className="icon fa fa-exclamation-triangle"
/>
<MemoizedFormattedMessage
defaultMessage="Link failed"
id="admin.group_settings.group_row.link_failed"
/>
</a>
</span>
<span
className="group-actions"
/>
<a
class="warning"
href="#"
>
<i
class="icon fa fa-exclamation-triangle"
/>
Link failed
</a>
</span>
<span
class="group-actions"
/>
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on linked and configured row 1`] = `
<div
className="group "
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group "
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check "
/>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
class="group-name"
>
<a
className=""
href="#"
onClick={[Function]}
>
<i
className="icon fa fa-link"
/>
<MemoizedFormattedMessage
defaultMessage="Linked"
id="admin.group_settings.group_row.linked"
/>
</a>
</span>
<span
className="group-actions"
<div
class="group-check "
/>
<span>
name
</span>
</div>
<div
class="group-content"
>
<Link
id="name_edit"
to="/admin_console/user_management/groups/group-id"
<span
class="group-description"
>
<MemoizedFormattedMessage
defaultMessage="Edit"
id="admin.group_settings.group_row.edit"
/>
</Link>
</span>
<a
class=""
href="#"
>
<i
class="icon fa fa-link"
/>
Linked
</a>
</span>
<span
class="group-actions"
>
<a
href="/admin_console/user_management/groups/group-id"
id="name_edit"
>
Edit
</a>
</span>
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on linked but not configured row 1`] = `
<div
className="group "
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group "
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check "
/>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
class="group-name"
>
<a
className=""
href="#"
onClick={[Function]}
>
<i
className="icon fa fa-link"
/>
<MemoizedFormattedMessage
defaultMessage="Linked"
id="admin.group_settings.group_row.linked"
/>
</a>
</span>
<span
className="group-actions"
<div
class="group-check "
/>
<span>
name
</span>
</div>
<div
class="group-content"
>
<Link
id="name_configure"
to="/admin_console/user_management/groups/group-id"
<span
class="group-description"
>
<MemoizedFormattedMessage
defaultMessage="Configure"
id="admin.group_settings.group_row.configure"
/>
</Link>
</span>
<a
class=""
href="#"
>
<i
class="icon fa fa-link"
/>
Linked
</a>
</span>
<span
class="group-actions"
>
<a
href="/admin_console/user_management/groups/group-id"
id="name_configure"
>
Configure
</a>
</span>
</div>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/GroupRow should match snapshot, on not linked row 1`] = `
<div
className="group "
id="name_group"
onClick={[Function]}
>
<div>
<div
className="group-row"
class="group "
id="name_group"
>
<div
className="group-name"
class="group-row"
>
<div
className="group-check "
/>
<span>
name
</span>
</div>
<div
className="group-content"
>
<span
className="group-description"
class="group-name"
>
<a
className=""
href="#"
onClick={[Function]}
<div
class="group-check "
/>
<span>
name
</span>
</div>
<div
class="group-content"
>
<span
class="group-description"
>
<i
className="icon fa fa-unlink"
/>
<MemoizedFormattedMessage
defaultMessage="Not Linked"
id="admin.group_settings.group_row.not_linked"
/>
</a>
</span>
<span
className="group-actions"
/>
<a
class=""
href="#"
>
<i
class="icon fa fa-unlink"
/>
Not Linked
</a>
</span>
<span
class="group-actions"
/>
</div>
</div>
</div>
</div>
@@ -1,64 +1,133 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/GroupSettings should match snapshot 1`] = `
<div
className="wrapper--fixed"
>
<AdminHeader>
<MemoizedFormattedMessage
defaultMessage="Groups"
id="admin.group_settings.groupsPageTitle"
/>
</AdminHeader>
<div>
<div
className="admin-console__wrapper"
class="wrapper--fixed"
>
<div
className="admin-console__content"
class="admin-console__header"
>
Groups
</div>
<div
class="admin-console__wrapper"
>
<div
className="banner info"
class="admin-console__content"
>
<div
className="banner__content"
class="banner info"
>
<MemoizedFormattedMessage
defaultMessage="Groups are a way to organize users and apply actions to all users within that group.
For more information on Groups, please see <link>documentation</link>."
id="admin.group_settings.introBanner"
values={
Object {
"link": [Function],
}
}
/>
<div
class="banner__content"
>
Groups are a way to organize users and apply actions to all users within that group.
For more information on Groups, please see
<a
href="https://mattermost.com/pl/default-ldap-group-sync?utm_source=mattermost&utm_medium=in-product&utm_content=group_settings&uid=&sid=&edition=team&server_version="
location="group_settings"
rel="noopener noreferrer"
target="_blank"
>
documentation
</a>
.
</div>
</div>
<div
class="AdminPanel clearfix "
id="ldap_groups"
>
<div
class="header"
>
<div>
<h3>
AD/LDAP Groups
</h3>
<div
class="mt-2"
>
Connect AD/LDAP and create groups in Mattermost. To get started, configure group attributes on the
<a
href="http://localhost:8065/admin_console/authentication/ldap"
location="group_settings.ldap_groups"
rel="noopener noreferrer"
target="_blank"
>
AD/LDAP
</a>
configuration page.
</div>
</div>
</div>
<div
class="groups-list"
>
<div
class="groups-list--global-actions"
>
<div
class="group-list-search"
>
<input
placeholder="Search"
type="text"
value=""
/>
<span
aria-hidden="true"
class="search__icon"
>
<svg
aria-label="Search Icon"
height="16px"
role="img"
viewBox="0 0 17 17"
width="16px"
>
<path
d="M16.398 15.138L13.014 11.754C13.458 11.178 13.8 10.548 14.04 9.86399C14.28 9.14399 14.4 8.40599 14.4 7.64999C14.4 6.42599 14.094 5.29199 13.482 4.24799C12.882 3.22799 12.072 2.41799 11.052 1.81799C10.008 1.20599 8.874 0.899988 7.65 0.899988C6.426 0.899988 5.292 1.20599 4.248 1.81799C3.228 2.41799 2.418 3.22799 1.818 4.24799C1.206 5.29199 0.9 6.42599 0.9 7.64999C0.9 8.87399 1.206 10.008 1.818 11.052C2.418 12.072 3.228 12.882 4.248 13.482C5.292 14.094 6.426 14.4 7.65 14.4C8.406 14.4 9.138 14.274 9.846 14.022C10.53 13.794 11.16 13.458 11.736 13.014L15.138 16.398C15.306 16.566 15.516 16.65 15.768 16.65C16.02 16.65 16.23 16.56 16.398 16.38C16.566 16.212 16.65 16.008 16.65 15.768C16.65 15.528 16.566 15.318 16.398 15.138ZM2.7 7.64999C2.7 6.74999 2.922 5.92199 3.366 5.16599C3.81 4.40999 4.41 3.80999 5.166 3.36599C5.922 2.92199 6.75 2.69999 7.65 2.69999C8.55 2.69999 9.378 2.92199 10.134 3.36599C10.89 3.80999 11.49 4.40999 11.934 5.16599C12.378 5.92199 12.6 6.74999 12.6 7.64999C12.6 8.54999 12.378 9.37799 11.934 10.134C11.49 10.89 10.89 11.49 10.134 11.934C9.378 12.378 8.55 12.6 7.65 12.6C6.75 12.6 5.922 12.378 5.166 11.934C4.41 11.49 3.81 10.89 3.366 10.134C2.922 9.37799 2.7 8.54999 2.7 7.64999Z"
/>
</svg>
</span>
<i
class="fa fa-times-circle group-filter-action hidden"
/>
<i
class="fa fa-caret-down group-filter-action "
/>
</div>
<div
class="group-list-link-unlink"
>
<button
class="btn btn-primary"
type="button"
>
<i
class="icon icon-link-variant"
/>
Link Selected Groups
</button>
</div>
</div>
<div
class="groups-list--body"
id="groups-list--body"
>
<div
class="groups-list-loading"
>
<i
class="fa fa-spinner fa-pulse fa-2x"
/>
</div>
</div>
</div>
</div>
</div>
<AdminPanel
id="ldap_groups"
subtitle={
Object {
"defaultMessage": "Connect AD/LDAP and create groups in Mattermost. To get started, configure group attributes on the <link>AD/LDAP</link> configuration page.",
"id": "admin.group_settings.ldapGroupsDescription",
}
}
subtitleValues={
Object {
"link": [Function],
}
}
title={
Object {
"defaultMessage": "AD/LDAP Groups",
"id": "admin.group_settings.ldapGroupsTitle",
}
}
>
<Connect(GroupsList) />
</AdminPanel>
<Pluggable
pluggableName="SystemConsoleGroupTable"
/>
</div>
</div>
</div>
@@ -1,40 +1,52 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/group_details/GroupProfile should match snapshot 1`] = `
<div
className="group-profile form-horizontal"
id="test"
>
<div>
<div
className="group-profile-field form-group mb-0"
class="group-profile form-horizontal"
id="test"
>
<label
className="control-label col-sm-4"
htmlFor="testInput"
>
<MemoizedFormattedMessage
defaultMessage="Name:"
id="admin.group_settings.group_details.group_profile.name"
/>
</label>
<div
className="col-sm-8"
class="group-profile-field form-group mb-0"
>
<div
className="icon-over-input"
<label
class="control-label col-sm-4"
for="testInput"
>
<MentionsIcon
aria-hidden="true"
className="icon icon__mentions"
Name:
</label>
<div
class="col-sm-8"
>
<div
class="icon-over-input"
>
<span
aria-hidden="true"
class="icon icon__mentions"
>
<svg
aria-label="Mention Icon"
fill="inherit"
height="12px"
role="img"
version="1.1"
viewBox="0 0 18 18"
width="12px"
>
<path
d="M9 11.7C9.72 11.7 10.35 11.43 10.89 10.89C11.43 10.35 11.7 9.71999 11.7 8.99999C11.7 8.27999 11.43 7.64999 10.89 7.10999C10.35 6.56999 9.72 6.29999 9 6.29999C8.28 6.29999 7.65 6.56999 7.11 7.10999C6.57 7.64999 6.3 8.27999 6.3 8.99999C6.3 9.71999 6.57 10.35 7.11 10.89C7.65 11.43 8.28 11.7 9 11.7ZM9 -1.26362e-05C11.46 -1.26362e-05 13.56 0.869987 15.3 2.60999C16.176 3.48599 16.842 4.45199 17.298 5.50799C17.766 6.59999 18 7.76399 18 8.99999V10.26C18 10.692 17.916 11.118 17.748 11.538C17.592 11.922 17.376 12.276 17.1 12.6C16.788 12.912 16.446 13.14 16.074 13.284C15.714 13.428 15.306 13.5 14.85 13.5C13.746 13.5 12.876 13.05 12.24 12.15C11.34 13.05 10.26 13.5 9 13.5C7.716 13.5 6.666 13.05 5.85 12.15C4.95 11.25 4.5 10.2 4.5 8.99999C4.5 7.70399 4.95 6.65399 5.85 5.84999C6.666 4.94999 7.716 4.49999 9 4.49999C10.296 4.49999 11.346 4.94999 12.15 5.84999C13.05 6.74999 13.5 7.79999 13.5 8.99999V10.26C13.5 10.44 13.536 10.614 13.608 10.782C13.692 10.95 13.8 11.106 13.932 11.25C14.076 11.382 14.232 11.49 14.4 11.574C14.568 11.658 14.718 11.7 14.85 11.7C15.03 11.7 15.204 11.664 15.372 11.592C15.54 11.508 15.696 11.394 15.84 11.25C16.08 11.01 16.2 10.68 16.2 10.26V8.99999C16.2 7.01999 15.48 5.30999 14.04 3.86999C13.332 3.16199 12.564 2.63999 11.736 2.30399C10.908 1.96799 9.996 1.79999 9 1.79999C7.056 1.79999 5.346 2.48999 3.87 3.86999C3.174 4.52999 2.652 5.30399 2.304 6.19199C1.968 7.04399 1.8 7.97999 1.8 8.99999C1.8 9.98399 1.974 10.902 2.322 11.754C2.67 12.594 3.186 13.356 3.87 14.04C4.554 14.724 5.328 15.252 6.192 15.624C7.092 16.008 8.028 16.2 9 16.2H13.5V18H9C7.752 18 6.6 17.772 5.544 17.316C4.488 16.872 3.51 16.2 2.61 15.3C1.71 14.4 1.05 13.44 0.63 12.42C0.21 11.4 0 10.266 0 9.01799C0 7.75799 0.222 6.59399 0.666 5.52599C1.098 4.49399 1.746 3.52199 2.61 2.60999C4.266 0.869987 6.396 -1.26362e-05 9 -1.26362e-05Z"
/>
</svg>
</span>
</div>
<input
class="form-control group-at-mention-input"
id="testInput"
type="text"
value="Test"
/>
</div>
<input
className="form-control group-at-mention-input"
disabled={false}
id="testInput"
type="text"
value="Test"
/>
</div>
</div>
</div>
@@ -1,115 +1,266 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/group_details/GroupProfileAndSettings should match snapshot, with toggle off 1`] = `
<AdminPanel
id="group_profile"
subtitle={
Object {
"defaultMessage": "The name for this group.",
"id": "admin.group_settings.group_detail.groupProfileDescription",
}
}
title={
Object {
"defaultMessage": "Group Profile",
"id": "admin.group_settings.group_detail.groupProfileTitle",
}
}
>
<Memo(GroupProfile)
customID="groupDisplayName"
isDisabled={true}
name="GroupProfileAndSettings"
showAtMention={false}
title={
Object {
"defaultMessage": "Name:",
"id": "admin.group_settings.group_details.group_profile.name",
}
}
/>
<div>
<div
className="group-settings"
class="AdminPanel clearfix "
id="group_profile"
>
<div
className="group-settings--body"
class="header"
>
<div>
<h3>
Group Profile
</h3>
<div
class="mt-2"
>
The name for this group.
</div>
</div>
</div>
<div
class="group-profile form-horizontal"
id="groupDisplayName"
>
<div
className="section-separator"
class="group-profile-field form-group mb-0"
>
<hr
className="separator__hr"
/>
<label
class="control-label col-sm-4"
for="groupDisplayNameInput"
>
Name:
</label>
<div
class="col-sm-8"
>
<div
class="icon-over-input"
/>
<input
class="form-control group-at-mention-input"
disabled=""
id="groupDisplayNameInput"
type="text"
value="GroupProfileAndSettings"
/>
</div>
</div>
</div>
<div
class="group-settings"
>
<div
class="group-settings--body"
>
<div
class="section-separator"
>
<hr
class="separator__hr"
/>
</div>
<div>
<div
class="line-switch d-flex flex-sm-column flex-md-row align-items-sm-start align-items-center"
>
<label
class="line-switch__label"
>
Enable Group Mentions
</label>
<div
class="line-switch__toggle"
data-testid="allowReferenceSwitch"
>
<button
aria-pressed="false"
class="btn btn-lg btn-toggle"
data-testid="allowReferenceSwitch-button"
id="allowReferenceSwitch"
tabindex="0"
type="button"
>
<div
class="handle"
/>
</button>
</div>
</div>
<div
class="row"
>
<div
class="col-sm-10"
>
<div
class="help-text-small help-text-no-padding "
>
When enabled, this group can be mentioned in other channels and teams. This may result in the group member list being visible to all users.
</div>
</div>
</div>
</div>
</div>
<GroupSettingsToggle
allowReference={false}
isDefault={false}
onToggle={[MockFunction]}
/>
</div>
</div>
</AdminPanel>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupProfileAndSettings should match snapshot, with toggle on 1`] = `
<AdminPanel
id="group_profile"
subtitle={
Object {
"defaultMessage": "The name for this group.",
"id": "admin.group_settings.group_detail.groupProfileDescription",
}
}
title={
Object {
"defaultMessage": "Group Profile",
"id": "admin.group_settings.group_detail.groupProfileTitle",
}
}
>
<Memo(GroupProfile)
customID="groupDisplayName"
isDisabled={true}
name="GroupProfileAndSettings"
showAtMention={false}
title={
Object {
"defaultMessage": "Name:",
"id": "admin.group_settings.group_details.group_profile.name",
}
}
/>
<div>
<div
className="group-settings"
class="AdminPanel clearfix "
id="group_profile"
>
<div
className="group-settings--body"
class="header"
>
<div>
<h3>
Group Profile
</h3>
<div
class="mt-2"
>
The name for this group.
</div>
</div>
</div>
<div
class="group-profile form-horizontal"
id="groupDisplayName"
>
<div
className="section-separator"
class="group-profile-field form-group mb-0"
>
<hr
className="separator__hr"
/>
<label
class="control-label col-sm-4"
for="groupDisplayNameInput"
>
Name:
</label>
<div
class="col-sm-8"
>
<div
class="icon-over-input"
/>
<input
class="form-control group-at-mention-input"
disabled=""
id="groupDisplayNameInput"
type="text"
value="GroupProfileAndSettings"
/>
</div>
</div>
</div>
<div
class="group-settings"
>
<div
class="group-settings--body"
>
<div
class="section-separator"
>
<hr
class="separator__hr"
/>
</div>
<div>
<div
class="line-switch d-flex flex-sm-column flex-md-row align-items-sm-start align-items-center"
>
<label
class="line-switch__label"
>
Enable Group Mentions
</label>
<div
class="line-switch__toggle"
data-testid="allowReferenceSwitch"
>
<button
aria-pressed="true"
class="btn btn-lg btn-toggle active"
data-testid="allowReferenceSwitch-button"
id="allowReferenceSwitch"
tabindex="0"
type="button"
>
<div
class="handle"
/>
</button>
</div>
</div>
<div
class="row"
>
<div
class="col-sm-10"
>
<div
class="help-text-small help-text-no-padding "
>
When enabled, this group can be mentioned in other channels and teams. This may result in the group member list being visible to all users.
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="group-profile form-horizontal"
id="groupMention"
>
<div
class="group-profile-field form-group mb-0"
>
<label
class="control-label col-sm-4"
for="groupMentionInput"
>
Group Mention:
</label>
<div
class="col-sm-8"
>
<div
class="icon-over-input"
>
<span
aria-hidden="true"
class="icon icon__mentions"
>
<svg
aria-label="Mention Icon"
fill="inherit"
height="12px"
role="img"
version="1.1"
viewBox="0 0 18 18"
width="12px"
>
<path
d="M9 11.7C9.72 11.7 10.35 11.43 10.89 10.89C11.43 10.35 11.7 9.71999 11.7 8.99999C11.7 8.27999 11.43 7.64999 10.89 7.10999C10.35 6.56999 9.72 6.29999 9 6.29999C8.28 6.29999 7.65 6.56999 7.11 7.10999C6.57 7.64999 6.3 8.27999 6.3 8.99999C6.3 9.71999 6.57 10.35 7.11 10.89C7.65 11.43 8.28 11.7 9 11.7ZM9 -1.26362e-05C11.46 -1.26362e-05 13.56 0.869987 15.3 2.60999C16.176 3.48599 16.842 4.45199 17.298 5.50799C17.766 6.59999 18 7.76399 18 8.99999V10.26C18 10.692 17.916 11.118 17.748 11.538C17.592 11.922 17.376 12.276 17.1 12.6C16.788 12.912 16.446 13.14 16.074 13.284C15.714 13.428 15.306 13.5 14.85 13.5C13.746 13.5 12.876 13.05 12.24 12.15C11.34 13.05 10.26 13.5 9 13.5C7.716 13.5 6.666 13.05 5.85 12.15C4.95 11.25 4.5 10.2 4.5 8.99999C4.5 7.70399 4.95 6.65399 5.85 5.84999C6.666 4.94999 7.716 4.49999 9 4.49999C10.296 4.49999 11.346 4.94999 12.15 5.84999C13.05 6.74999 13.5 7.79999 13.5 8.99999V10.26C13.5 10.44 13.536 10.614 13.608 10.782C13.692 10.95 13.8 11.106 13.932 11.25C14.076 11.382 14.232 11.49 14.4 11.574C14.568 11.658 14.718 11.7 14.85 11.7C15.03 11.7 15.204 11.664 15.372 11.592C15.54 11.508 15.696 11.394 15.84 11.25C16.08 11.01 16.2 10.68 16.2 10.26V8.99999C16.2 7.01999 15.48 5.30999 14.04 3.86999C13.332 3.16199 12.564 2.63999 11.736 2.30399C10.908 1.96799 9.996 1.79999 9 1.79999C7.056 1.79999 5.346 2.48999 3.87 3.86999C3.174 4.52999 2.652 5.30399 2.304 6.19199C1.968 7.04399 1.8 7.97999 1.8 8.99999C1.8 9.98399 1.974 10.902 2.322 11.754C2.67 12.594 3.186 13.356 3.87 14.04C4.554 14.724 5.328 15.252 6.192 15.624C7.092 16.008 8.028 16.2 9 16.2H13.5V18H9C7.752 18 6.6 17.772 5.544 17.316C4.488 16.872 3.51 16.2 2.61 15.3C1.71 14.4 1.05 13.44 0.63 12.42C0.21 11.4 0 10.266 0 9.01799C0 7.75799 0.222 6.59399 0.666 5.52599C1.098 4.49399 1.746 3.52199 2.61 2.60999C4.266 0.869987 6.396 -1.26362e-05 9 -1.26362e-05Z"
/>
</svg>
</span>
</div>
<input
class="form-control group-at-mention-input"
id="groupMentionInput"
type="text"
value="GroupProfileAndSettings"
/>
</div>
</div>
<GroupSettingsToggle
allowReference={true}
isDefault={false}
onToggle={[MockFunction]}
/>
</div>
</div>
<Memo(GroupProfile)
customID="groupMention"
name="GroupProfileAndSettings"
onChange={[MockFunction]}
showAtMention={true}
title={
Object {
"defaultMessage": "Group Mention:",
"id": "admin.group_settings.group_details.group_mention.name",
}
}
/>
</AdminPanel>
</div>
`;
@@ -1,176 +1,421 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannels should match snapshot, with teams, with channels and loaded 1`] = `
<div
className="AdminPanel__content"
>
<table
className="AdminPanel__table group-teams-and-channels"
id="team_and_channel_membership_table"
<div>
<div
class="AdminPanel__content"
>
<thead
className="group-teams-and-channels--header"
<table
class="AdminPanel__table group-teams-and-channels"
id="team_and_channel_membership_table"
>
<tr>
<th
style={
Object {
"width": "30%",
}
}
>
<MemoizedFormattedMessage
defaultMessage="Name"
id="admin.group_settings.group_profile.group_teams_and_channels.name"
<thead
class="group-teams-and-channels--header"
>
<tr>
<th
style="width: 30%;"
>
Name
</th>
<th
style="width: 25%;"
>
Type
</th>
<th
style="width: 25%;"
>
Assigned Roles
</th>
<th
style="width: 20%;"
/>
</th>
<th
style={
Object {
"width": "25%",
}
}
</tr>
</thead>
<tbody
class="group-teams-and-channels--body"
>
<tr
class="group-teams-and-channels-row has-children"
>
<MemoizedFormattedMessage
defaultMessage="Type"
id="admin.group_settings.group_profile.group_teams_and_channels.type"
/>
</th>
<th
style={
Object {
"width": "25%",
}
}
<td>
<span
class="arrow-icon"
>
<i
class="fa fa-caret-down"
/>
</span>
<span
class=""
>
Team 1
</span>
</td>
<td
class="type"
>
Team
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Team 1_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row"
>
<MemoizedFormattedMessage
defaultMessage="Assigned Roles"
id="admin.group_settings.group_profile.group_teams_and_channels.assignedRoles"
/>
</th>
<th
style={
Object {
"width": "20%",
}
}
/>
</tr>
</thead>
<tbody
className="group-teams-and-channels--body"
>
<GroupTeamsAndChannelsRow
hasChildren={true}
id="11111111111111111111111111"
key="11111111111111111111111111"
name="Team 1"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="public-team"
/>
<GroupTeamsAndChannelsRow
id="44444444444444444444444444"
key="44444444444444444444444444"
name="Channel 4"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="public-channel"
/>
<GroupTeamsAndChannelsRow
hasChildren={false}
id="22222222222222222222222222"
key="22222222222222222222222222"
name="Team 2"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="private-team"
/>
<GroupTeamsAndChannelsRow
hasChildren={false}
id="33333333333333333333333333"
key="33333333333333333333333333"
name="Team 3"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="private-team"
/>
<GroupTeamsAndChannelsRow
hasChildren={true}
id="99999999999999999999999999"
key="99999999999999999999999999"
name="Team 9"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="public-team"
/>
<GroupTeamsAndChannelsRow
id="55555555555555555555555555"
key="55555555555555555555555555"
name="Channel 5"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="private-channel"
/>
<GroupTeamsAndChannelsRow
id="55555555555555555555555555"
key="55555555555555555555555555"
name="Channel 5"
onChangeRoles={[Function]}
onRemoveItem={[Function]}
onToggleCollapse={[Function]}
type="private-channel"
/>
</tbody>
</table>
<td>
<span
class="arrow-icon"
/>
<span
class="channel-icon"
>
<span
class="icon icon__globe"
>
<svg
aria-label="Public Channel Icon"
height="13px"
role="presentation"
viewBox="0 0 16 16"
width="13px"
>
<path
d="M8 -2.47955e-05C6.56 -2.47955e-05 5.216 0.367975 3.968 1.10398C2.76267 1.80798 1.808 2.76264 1.104 3.96797C0.368 5.21598 0 6.55998 0 7.99998C0 9.43998 0.368 10.784 1.104 12.032C1.808 13.2373 2.76267 14.192 3.968 14.896C5.216 15.632 6.56 16 8 16C9.44 16 10.784 15.632 12.032 14.896C13.2373 14.192 14.192 13.2373 14.896 12.032C15.632 10.784 16 9.43998 16 7.99998C16 6.55998 15.632 5.21598 14.896 3.96797C14.192 2.76264 13.2373 1.80798 12.032 1.10398C10.784 0.367975 9.44 -2.47955e-05 8 -2.47955e-05ZM14.4 8.39998C14.2507 8.59198 13.92 8.80531 13.408 9.03998C12.768 9.31731 12.032 9.53064 11.2 9.67998V6.95998C12.288 6.77864 13.168 6.53864 13.84 6.23998C13.872 6.20798 13.9253 6.18131 14 6.15998C14.0853 6.12798 14.1387 6.10131 14.16 6.07998C14.32 6.63464 14.4 7.27464 14.4 7.99998V8.39998ZM6.8 11.52C6.93867 11.52 7.14133 11.536 7.408 11.568C7.67467 11.5893 7.872 11.6 8 11.6C8.53333 11.6 8.93333 11.5733 9.2 11.52C9.008 12.2773 8.8 12.912 8.576 13.424C8.36267 13.8933 8.17067 14.192 8 14.32C7.808 14.1813 7.61067 13.8826 7.408 13.424C7.152 12.8906 6.94933 12.256 6.8 11.52ZM8 9.99998C7.63733 9.99998 7.376 9.99464 7.216 9.98398C6.93867 9.97331 6.69333 9.95198 6.48 9.91998C6.48 9.74931 6.46933 9.46664 6.448 9.07198C6.416 8.61331 6.4 8.25598 6.4 7.99998V7.11998C6.77333 7.17331 7.30667 7.19998 8 7.19998C8.69333 7.19998 9.22667 7.17331 9.6 7.11998V7.99998C9.6 8.25598 9.584 8.61331 9.552 9.07198C9.53067 9.46664 9.52 9.74931 9.52 9.91998C9.30667 9.95198 9.06133 9.97331 8.784 9.98398C8.624 9.99464 8.36267 9.99998 8 9.99998ZM9.44 5.51998C9.12 5.57331 8.64 5.59998 8 5.59998C7.36 5.59998 6.88 5.57331 6.56 5.51998C6.656 4.82664 6.8 4.18131 6.992 3.58397C7.14133 3.07198 7.31733 2.62931 7.52 2.25597C7.69067 1.93598 7.85067 1.71731 8 1.59997C8.14933 1.71731 8.30933 1.93598 8.48 2.25597C8.68267 2.62931 8.85867 3.07198 9.008 3.58397C9.2 4.18131 9.344 4.82664 9.44 5.51998ZM13.44 4.63998C12.5867 5.00264 11.7867 5.24264 11.04 5.35997C10.9653 4.71997 10.848 4.11198 10.688 3.53598C10.5387 2.95998 10.3627 2.44798 10.16 1.99998C11.5573 2.49064 12.6507 3.37064 13.44 4.63998ZM2.56 4.63998C2.976 4.02131 3.456 3.49331 4 3.05597C4.576 2.58664 5.216 2.23464 5.92 1.99998C5.52533 2.87464 5.20533 3.99464 4.96 5.35997C3.92533 5.11464 3.12533 4.87464 2.56 4.63998ZM1.92 6.07998C1.952 6.11198 2.00533 6.14398 2.08 6.17598L2.24 6.23998C2.96533 6.55998 3.84533 6.79998 4.88 6.95998C4.848 7.09864 4.82667 7.26398 4.816 7.45598C4.80533 7.56264 4.8 7.74398 4.8 7.99998C4.8 8.74664 4.82667 9.30664 4.88 9.67998C4.048 9.53064 3.31733 9.31731 2.688 9.03998C2.16533 8.80531 1.82933 8.59198 1.68 8.39998V7.99998C1.63733 7.67998 1.63733 7.34931 1.68 7.00798C1.72267 6.66664 1.80267 6.35731 1.92 6.07998ZM2.16 10.56C3.14133 10.9546 4.128 11.2213 5.12 11.36C5.248 12.2133 5.51467 13.0933 5.92 14C5.088 13.7226 4.34133 13.2853 3.68 12.688C3.02933 12.08 2.52267 11.3706 2.16 10.56ZM13.84 10.56C13.4773 11.3706 12.9653 12.08 12.304 12.688C11.6533 13.2853 10.912 13.7226 10.08 14C10.4853 13.0933 10.752 12.2133 10.88 11.36C12.1493 11.1786 13.136 10.912 13.84 10.56Z"
/>
</svg>
</span>
</span>
<span
class=""
>
Channel 4
</span>
</td>
<td
class="type"
>
Channel
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Channel 4_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="name-no-arrow"
>
Team 2
</span>
</td>
<td
class="type"
>
Team (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Team 2_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="name-no-arrow"
>
Team 3
</span>
</td>
<td
class="type"
>
Team (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Team 3_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row has-children"
>
<td>
<span
class="arrow-icon"
>
<i
class="fa fa-caret-down"
/>
</span>
<span
class=""
>
Team 9
</span>
</td>
<td
class="type"
>
Team
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Team 9_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="channel-icon"
>
<span
class="icon icon__lock"
>
<svg
aria-label="Private Channel Icon"
height="13px"
role="presentation"
viewBox="0 0 13 15"
width="12px"
>
<g
fill="inherit"
fill-rule="evenodd"
stroke="none"
stroke-width="1"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-116.000000, -175.000000)"
>
<g
transform="translate(95.000000, 0.000000)"
>
<g
transform="translate(20.000000, 113.000000)"
>
<g
transform="translate(1.000000, 62.000000)"
>
<path
d="M12.0714286,6.5 L11.1428571,6.5 L11.1428571,4.64285714 C11.1428571,2.07814286 9.06471429,0 6.5,0 C3.93528571,0 1.85714286,2.07814286 1.85714286,4.64285714 L1.85714286,6.5 L0.928571429,6.5 C0.415071429,6.5 0,7.00792857 0,7.52142857 L0,13.9285714 C0,14.4420714 0.415071429,14.8571429 0.928571429,14.8571429 L12.0714286,14.8571429 C12.5849286,14.8571429 13,14.4420714 13,13.9285714 L13,7.52142857 C13,7.00792857 12.5849286,6.5 12.0714286,6.5 Z M6.5,1.85714286 C8.03585714,1.85714286 9.28571429,3.107 9.28571429,4.64285714 L9.28571429,6.5 L8.35714286,6.5 L4.64285714,6.5 L3.71428571,6.5 L3.71428571,4.64285714 C3.71428571,3.107 4.96414286,1.85714286 6.5,1.85714286 Z"
/>
</g>
</g>
</g>
</g>
</g>
</svg>
</span>
</span>
<span
class=""
>
Channel 5
</span>
</td>
<td
class="type"
>
Channel (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Channel 5_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="channel-icon"
>
<span
class="icon icon__lock"
>
<svg
aria-label="Private Channel Icon"
height="13px"
role="presentation"
viewBox="0 0 13 15"
width="12px"
>
<g
fill="inherit"
fill-rule="evenodd"
stroke="none"
stroke-width="1"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-116.000000, -175.000000)"
>
<g
transform="translate(95.000000, 0.000000)"
>
<g
transform="translate(20.000000, 113.000000)"
>
<g
transform="translate(1.000000, 62.000000)"
>
<path
d="M12.0714286,6.5 L11.1428571,6.5 L11.1428571,4.64285714 C11.1428571,2.07814286 9.06471429,0 6.5,0 C3.93528571,0 1.85714286,2.07814286 1.85714286,4.64285714 L1.85714286,6.5 L0.928571429,6.5 C0.415071429,6.5 0,7.00792857 0,7.52142857 L0,13.9285714 C0,14.4420714 0.415071429,14.8571429 0.928571429,14.8571429 L12.0714286,14.8571429 C12.5849286,14.8571429 13,14.4420714 13,13.9285714 L13,7.52142857 C13,7.00792857 12.5849286,6.5 12.0714286,6.5 Z M6.5,1.85714286 C8.03585714,1.85714286 9.28571429,3.107 9.28571429,4.64285714 L9.28571429,6.5 L8.35714286,6.5 L4.64285714,6.5 L3.71428571,6.5 L3.71428571,4.64285714 C3.71428571,3.107 4.96414286,1.85714286 6.5,1.85714286 Z"
/>
</g>
</g>
</g>
</g>
</g>
</svg>
</span>
</span>
<span
class=""
>
Channel 6
</span>
</td>
<td
class="type"
>
Channel (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Channel 6_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannels should match snapshot, with teams, with channels and loading 1`] = `
<div
className="group-teams-and-channels"
>
<div>
<div
className="group-teams-and-channels-loading"
class="group-teams-and-channels"
>
<i
className="fa fa-spinner fa-pulse fa-2x"
/>
<div
class="group-teams-and-channels-loading"
>
<i
class="fa fa-spinner fa-pulse fa-2x"
/>
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannels should match snapshot, without teams, without channels and loaded 1`] = `
<div
className="group-teams-and-channels"
>
<div>
<div
className="group-teams-and-channels-empty"
class="group-teams-and-channels"
>
<MemoizedFormattedMessage
defaultMessage="No teams or channels specified yet"
id="admin.group_settings.group_details.group_teams_and_channels.no-teams-or-channels-speicified"
/>
<div
class="group-teams-and-channels-empty"
>
No teams or channels specified yet
</div>
</div>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannels should match snapshot, without teams, without channels and loading 1`] = `
<div
className="group-teams-and-channels"
>
<div>
<div
className="group-teams-and-channels-loading"
class="group-teams-and-channels"
>
<i
className="fa fa-spinner fa-pulse fa-2x"
/>
<div
class="group-teams-and-channels-loading"
>
<i
class="fa fa-spinner fa-pulse fa-2x"
/>
</div>
</div>
</div>
`;
@@ -1,481 +1,312 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, for private-channel 1`] = `
<tr
className="group-teams-and-channels-row"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "channel",
"name": "Test private-channel",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "channel",
"name": "Test private-channel",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
/>
<span
className="channel-icon"
>
<LockIcon
className="icon icon__lock"
/>
</span>
<span
className=""
>
Test private-channel
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Channel (Private)"
id="admin.group_settings.group_details.group_teams_and_channels_row.privateChannel"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test private-channel_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="channel-icon"
>
<span
class="icon icon__lock"
>
<svg
aria-label="Private Channel Icon"
height="13px"
role="presentation"
viewBox="0 0 13 15"
width="12px"
>
<g
fill="inherit"
fill-rule="evenodd"
stroke="none"
stroke-width="1"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-116.000000, -175.000000)"
>
<g
transform="translate(95.000000, 0.000000)"
>
<g
transform="translate(20.000000, 113.000000)"
>
<g
transform="translate(1.000000, 62.000000)"
>
<path
d="M12.0714286,6.5 L11.1428571,6.5 L11.1428571,4.64285714 C11.1428571,2.07814286 9.06471429,0 6.5,0 C3.93528571,0 1.85714286,2.07814286 1.85714286,4.64285714 L1.85714286,6.5 L0.928571429,6.5 C0.415071429,6.5 0,7.00792857 0,7.52142857 L0,13.9285714 C0,14.4420714 0.415071429,14.8571429 0.928571429,14.8571429 L12.0714286,14.8571429 C12.5849286,14.8571429 13,14.4420714 13,13.9285714 L13,7.52142857 C13,7.00792857 12.5849286,6.5 12.0714286,6.5 Z M6.5,1.85714286 C8.03585714,1.85714286 9.28571429,3.107 9.28571429,4.64285714 L9.28571429,6.5 L8.35714286,6.5 L4.64285714,6.5 L3.71428571,6.5 L3.71428571,4.64285714 C3.71428571,3.107 4.96414286,1.85714286 6.5,1.85714286 Z"
/>
</g>
</g>
</g>
</g>
</g>
</svg>
</span>
</span>
<span
class=""
>
Test private-channel
</span>
</td>
<td
class="type"
>
Channel (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test private-channel_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, for private-team 1`] = `
<tr
className="group-teams-and-channels-row"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "team",
"name": "Test private-team",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "team",
"name": "Test private-team",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
/>
<span
className="name-no-arrow"
>
Test private-team
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Team (Private)"
id="admin.group_settings.group_details.group_teams_and_channels_row.privateTeam"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test private-team_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="name-no-arrow"
>
Test private-team
</span>
</td>
<td
class="type"
>
Team (Private)
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test private-team_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, for public-channel 1`] = `
<tr
className="group-teams-and-channels-row"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "channel",
"name": "Test public-channel",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "channel",
"name": "Test public-channel",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
/>
<span
className="channel-icon"
>
<GlobeIcon
className="icon icon__globe"
/>
</span>
<span
className=""
>
Test public-channel
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Channel"
id="admin.group_settings.group_details.group_teams_and_channels_row.publicChannel"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test public-channel_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="channel-icon"
>
<span
class="icon icon__globe"
>
<svg
aria-label="Public Channel Icon"
height="13px"
role="presentation"
viewBox="0 0 16 16"
width="13px"
>
<path
d="M8 -2.47955e-05C6.56 -2.47955e-05 5.216 0.367975 3.968 1.10398C2.76267 1.80798 1.808 2.76264 1.104 3.96797C0.368 5.21598 0 6.55998 0 7.99998C0 9.43998 0.368 10.784 1.104 12.032C1.808 13.2373 2.76267 14.192 3.968 14.896C5.216 15.632 6.56 16 8 16C9.44 16 10.784 15.632 12.032 14.896C13.2373 14.192 14.192 13.2373 14.896 12.032C15.632 10.784 16 9.43998 16 7.99998C16 6.55998 15.632 5.21598 14.896 3.96797C14.192 2.76264 13.2373 1.80798 12.032 1.10398C10.784 0.367975 9.44 -2.47955e-05 8 -2.47955e-05ZM14.4 8.39998C14.2507 8.59198 13.92 8.80531 13.408 9.03998C12.768 9.31731 12.032 9.53064 11.2 9.67998V6.95998C12.288 6.77864 13.168 6.53864 13.84 6.23998C13.872 6.20798 13.9253 6.18131 14 6.15998C14.0853 6.12798 14.1387 6.10131 14.16 6.07998C14.32 6.63464 14.4 7.27464 14.4 7.99998V8.39998ZM6.8 11.52C6.93867 11.52 7.14133 11.536 7.408 11.568C7.67467 11.5893 7.872 11.6 8 11.6C8.53333 11.6 8.93333 11.5733 9.2 11.52C9.008 12.2773 8.8 12.912 8.576 13.424C8.36267 13.8933 8.17067 14.192 8 14.32C7.808 14.1813 7.61067 13.8826 7.408 13.424C7.152 12.8906 6.94933 12.256 6.8 11.52ZM8 9.99998C7.63733 9.99998 7.376 9.99464 7.216 9.98398C6.93867 9.97331 6.69333 9.95198 6.48 9.91998C6.48 9.74931 6.46933 9.46664 6.448 9.07198C6.416 8.61331 6.4 8.25598 6.4 7.99998V7.11998C6.77333 7.17331 7.30667 7.19998 8 7.19998C8.69333 7.19998 9.22667 7.17331 9.6 7.11998V7.99998C9.6 8.25598 9.584 8.61331 9.552 9.07198C9.53067 9.46664 9.52 9.74931 9.52 9.91998C9.30667 9.95198 9.06133 9.97331 8.784 9.98398C8.624 9.99464 8.36267 9.99998 8 9.99998ZM9.44 5.51998C9.12 5.57331 8.64 5.59998 8 5.59998C7.36 5.59998 6.88 5.57331 6.56 5.51998C6.656 4.82664 6.8 4.18131 6.992 3.58397C7.14133 3.07198 7.31733 2.62931 7.52 2.25597C7.69067 1.93598 7.85067 1.71731 8 1.59997C8.14933 1.71731 8.30933 1.93598 8.48 2.25597C8.68267 2.62931 8.85867 3.07198 9.008 3.58397C9.2 4.18131 9.344 4.82664 9.44 5.51998ZM13.44 4.63998C12.5867 5.00264 11.7867 5.24264 11.04 5.35997C10.9653 4.71997 10.848 4.11198 10.688 3.53598C10.5387 2.95998 10.3627 2.44798 10.16 1.99998C11.5573 2.49064 12.6507 3.37064 13.44 4.63998ZM2.56 4.63998C2.976 4.02131 3.456 3.49331 4 3.05597C4.576 2.58664 5.216 2.23464 5.92 1.99998C5.52533 2.87464 5.20533 3.99464 4.96 5.35997C3.92533 5.11464 3.12533 4.87464 2.56 4.63998ZM1.92 6.07998C1.952 6.11198 2.00533 6.14398 2.08 6.17598L2.24 6.23998C2.96533 6.55998 3.84533 6.79998 4.88 6.95998C4.848 7.09864 4.82667 7.26398 4.816 7.45598C4.80533 7.56264 4.8 7.74398 4.8 7.99998C4.8 8.74664 4.82667 9.30664 4.88 9.67998C4.048 9.53064 3.31733 9.31731 2.688 9.03998C2.16533 8.80531 1.82933 8.59198 1.68 8.39998V7.99998C1.63733 7.67998 1.63733 7.34931 1.68 7.00798C1.72267 6.66664 1.80267 6.35731 1.92 6.07998ZM2.16 10.56C3.14133 10.9546 4.128 11.2213 5.12 11.36C5.248 12.2133 5.51467 13.0933 5.92 14C5.088 13.7226 4.34133 13.2853 3.68 12.688C3.02933 12.08 2.52267 11.3706 2.16 10.56ZM13.84 10.56C13.4773 11.3706 12.9653 12.08 12.304 12.688C11.6533 13.2853 10.912 13.7226 10.08 14C10.4853 13.0933 10.752 12.2133 10.88 11.36C12.1493 11.1786 13.136 10.912 13.84 10.56Z"
/>
</svg>
</span>
</span>
<span
class=""
>
Test public-channel
</span>
</td>
<td
class="type"
>
Channel
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test public-channel_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, for public-team 1`] = `
<tr
className="group-teams-and-channels-row"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "team",
"name": "Test public-team",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "team",
"name": "Test public-team",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
/>
<span
className="name-no-arrow"
>
Test public-team
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Team"
id="admin.group_settings.group_details.group_teams_and_channels_row.publicTeam"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test public-team_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row"
>
<td>
<span
class="arrow-icon"
/>
<span
class="name-no-arrow"
>
Test public-team
</span>
</td>
<td
class="type"
>
Team
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test public-team_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, when has children 1`] = `
<tr
className="group-teams-and-channels-row has-children"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "team",
"name": "Test team with children",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "team",
"name": "Test team with children",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
>
<i
className="fa fa-caret-down"
onClick={[Function]}
/>
</span>
<span
className=""
>
Test team with children
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Team"
id="admin.group_settings.group_details.group_teams_and_channels_row.publicTeam"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test team with children_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row has-children"
>
<td>
<span
class="arrow-icon"
>
<i
class="fa fa-caret-down"
/>
</span>
<span
class=""
>
Test team with children
</span>
</td>
<td
class="type"
>
Team
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test team with children_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
exports[`components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow should match snapshot, when has children and is collapsed 1`] = `
<tr
className="group-teams-and-channels-row has-children collapsed"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_button"
/>
}
message={
<Memo(MemoizedFormattedMessage)
defaultMessage="Removing this membership will prevent future users in this group from being added to the {name} {displayType}."
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_body"
values={
Object {
"displayType": "team",
"name": "Test team with children",
}
}
/>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove Membership from the {name} {displayType}?"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove.confirm_header"
values={
Object {
"displayType": "team",
"name": "Test team with children",
}
}
/>
}
/>
<td>
<span
className="arrow-icon"
>
<i
className="fa fa-caret-right"
onClick={[Function]}
/>
</span>
<span
className=""
>
Test team with children
</span>
</td>
<td
className="type"
>
<MemoizedFormattedMessage
defaultMessage="Team"
id="admin.group_settings.group_details.group_teams_and_channels_row.publicTeam"
/>
</td>
<td />
<td
className="text-right"
>
<button
className="btn btn-tertiary"
data-testid="Test team with children_groupsyncable_remove"
onClick={[Function]}
type="button"
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.group_settings.group_details.group_teams_and_channels_row.remove"
/>
</button>
</td>
</tr>
<div>
<table>
<tbody>
<tr
class="group-teams-and-channels-row has-children collapsed"
>
<td>
<span
class="arrow-icon"
>
<i
class="fa fa-caret-right"
/>
</span>
<span
class=""
>
Test team with children
</span>
</td>
<td
class="type"
>
Team
</td>
<td />
<td
class="text-right"
>
<button
class="btn btn-tertiary"
data-testid="Test team with children_groupsyncable_remove"
type="button"
>
Remove
</button>
</td>
</tr>
</tbody>
</table>
</div>
`;
@@ -1,47 +0,0 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/group_settings/group_details/GroupUsersRow should match snapshot 1`] = `
<div
className="group-users-row"
>
<Memo(Avatar)
size="lg"
url="/api/v4/users/xxxxxxxxxxxxxxxxxxxxxxxxxx/image"
username="test"
/>
<div
className="user-data"
>
<div
className="name-row"
>
<span
className="username"
>
@test
</span>
-
<span
className="display-name"
>
Test display name
</span>
</div>
<div>
<span
className="email-label"
>
<MemoizedFormattedMessage
defaultMessage="Email:"
id="admin.group_settings.group_details.group_users.email"
/>
</span>
<span
className="email"
>
test@test.com
</span>
</div>
</div>
</div>
`;
@@ -1,22 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupProfile from 'components/admin_console/group_settings/group_details/group_profile';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/group_details/GroupProfile', () => {
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupProfile
customID='test'
isDisabled={false}
name='Test'
showAtMention={true}
title={{id: 'admin.group_settings.group_details.group_profile.name', defaultMessage: 'Name:'}}
onChange={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByLabelText('Name:')).toBeInTheDocument();
expect(screen.getByDisplayValue('Test')).not.toBeDisabled();
expect(container.querySelector('.icon__mentions')).toBeInTheDocument();
});
});
@@ -1,14 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {GroupProfileAndSettings} from './group_profile_and_settings';
describe('components/admin_console/group_settings/group_details/GroupProfileAndSettings', () => {
test('should match snapshot, with toggle off', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupProfileAndSettings
displayname='GroupProfileAndSettings'
mentionname='GroupProfileAndSettings'
@@ -17,11 +18,16 @@ describe('components/admin_console/group_settings/group_details/GroupProfileAndS
onToggle={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('Enable Group Mentions')).toBeInTheDocument();
// Group Mention field should not be visible when toggle is off
expect(screen.queryByText('Group Mention:')).not.toBeInTheDocument();
});
test('should match snapshot, with toggle on', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupProfileAndSettings
displayname='GroupProfileAndSettings'
mentionname='GroupProfileAndSettings'
@@ -30,6 +36,14 @@ describe('components/admin_console/group_settings/group_details/GroupProfileAndS
onToggle={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
// Group Mention field should be visible when toggle is on
expect(screen.getByText('Group Mention:')).toBeInTheDocument();
// Both inputs should be present (displayname and mentionname)
const inputs = screen.getAllByDisplayValue('GroupProfileAndSettings');
expect(inputs).toHaveLength(2);
});
});
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupTeamsAndChannels from 'components/admin_console/group_settings/group_details/group_teams_and_channels';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/group_details/GroupTeamsAndChannels', () => {
const defaultProps = {
id: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
@@ -47,9 +48,9 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
team_id: '99999999999999999999999999',
team_type: 'O',
team_display_name: 'Team 9',
channel_id: '55555555555555555555555555',
channel_id: '66666666666666666666666666',
channel_type: 'P',
channel_display_name: 'Channel 5',
channel_display_name: 'Channel 6',
},
],
loading: false,
@@ -60,27 +61,27 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
};
test('should match snapshot, with teams, with channels and loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
loading={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, with teams, with channels and loaded', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
loading={false}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, without teams, without channels and loading', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
teams={[]}
@@ -88,11 +89,11 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
loading={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, without teams, without channels and loaded', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
teams={[]}
@@ -100,45 +101,52 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
loading={false}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should toggle the collapse for an element', () => {
const wrapper = shallow<GroupTeamsAndChannels>(<GroupTeamsAndChannels {...defaultProps}/>);
const instance = wrapper.instance();
expect(
Boolean(wrapper.state().collapsed['11111111111111111111111111']),
).toBe(false);
expect(
Boolean(wrapper.state().collapsed['22222222222222222222222222']),
).toBe(false);
instance.onToggleCollapse('11111111111111111111111111');
expect(
Boolean(wrapper.state().collapsed['11111111111111111111111111']),
).toBe(true);
expect(
Boolean(wrapper.state().collapsed['22222222222222222222222222']),
).toBe(false);
instance.onToggleCollapse('11111111111111111111111111');
expect(
Boolean(wrapper.state().collapsed['11111111111111111111111111']),
).toBe(false);
expect(
Boolean(wrapper.state().collapsed['22222222222222222222222222']),
).toBe(false);
test('should toggle the collapse for an element', async () => {
renderWithContext(<GroupTeamsAndChannels {...defaultProps}/>);
// Team 1 has children (Channel 4), so it should have a caret icon
// Initially not collapsed, so caret-down is shown and channel is visible
expect(screen.getByText('Channel 4')).toBeInTheDocument();
// Find the caret for Team 1 and click it to collapse
const caretDown = document.querySelector('.fa-caret-down');
expect(caretDown).toBeInTheDocument();
await userEvent.click(caretDown!);
// After collapsing Team 1, Channel 4 should no longer be visible
expect(screen.queryByText('Channel 4')).not.toBeInTheDocument();
// Click again to expand
const caretRight = document.querySelectorAll('.fa-caret-right');
// Find the caret that belongs to Team 1 (the first one with caret-right within team rows)
await userEvent.click(caretRight[0]);
// Channel 4 should be visible again
expect(screen.getByText('Channel 4')).toBeInTheDocument();
});
test('should invoke the onRemoveItem callback', async () => {
const onRemoveItem = jest.fn();
const wrapper = shallow<GroupTeamsAndChannels>(
renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
onChangeRoles={jest.fn()}
onRemoveItem={onRemoveItem}
/>,
);
const instance = wrapper.instance();
instance.onRemoveItem('11111111111111111111111111', 'public-team');
// Click the remove button for Team 1
const removeButton = screen.getByTestId('Team 1_groupsyncable_remove');
await userEvent.click(removeButton);
// Confirm the removal in the modal
const confirmButton = screen.getByText('Yes, Remove');
await userEvent.click(confirmButton);
expect(onRemoveItem).toHaveBeenCalledWith(
'11111111111111111111111111',
'public-team',
@@ -147,19 +155,31 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
test('should invoke the onChangeRoles callback', async () => {
const onChangeRoles = jest.fn();
const wrapper = shallow<GroupTeamsAndChannels>(
renderWithContext(
<GroupTeamsAndChannels
{...defaultProps}
teams={[
{
team_id: '11111111111111111111111111',
team_type: 'O',
team_display_name: 'Team 1',
scheme_admin: false,
},
]}
channels={[]}
onChangeRoles={onChangeRoles}
onRemoveItem={jest.fn()}
/>,
);
const instance = wrapper.instance();
instance.onChangeRoles(
'11111111111111111111111111',
'public-team',
true,
);
// Click on the current role dropdown for Team 1
const roleDropdown = screen.getByTestId('Team 1_current_role');
await userEvent.click(roleDropdown);
// Click the role option to change (Team Admin)
const roleOption = screen.getByText('Team Admin');
await userEvent.click(roleOption);
expect(onChangeRoles).toHaveBeenCalledWith(
'11111111111111111111111111',
'public-team',
@@ -1,12 +1,23 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupTeamsAndChannelsRow from 'components/admin_console/group_settings/group_details/group_teams_and_channels_row';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/group_details/GroupTeamsAndChannelsRow', () => {
const renderInTable = (ui: React.ReactElement) => {
return renderWithContext(
<table>
<tbody>
{ui}
</tbody>
</table>,
);
};
for (const type of [
'public-team',
'private-team',
@@ -14,7 +25,7 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
'private-channel',
]) {
test('should match snapshot, for ' + type, () => {
const wrapper = shallow(
const {container} = renderInTable(
<GroupTeamsAndChannelsRow
id='xxxxxxxxxxxxxxxxxxxxxxxxxx'
type={type}
@@ -26,11 +37,11 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
onChangeRoles={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
}
test('should match snapshot, when has children', () => {
const wrapper = shallow(
const {container} = renderInTable(
<GroupTeamsAndChannelsRow
id='xxxxxxxxxxxxxxxxxxxxxxxxxx'
type='public-team'
@@ -42,11 +53,11 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
onChangeRoles={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, when has children and is collapsed', () => {
const wrapper = shallow(
const {container} = renderInTable(
<GroupTeamsAndChannelsRow
id='xxxxxxxxxxxxxxxxxxxxxxxxxx'
type='public-team'
@@ -58,12 +69,12 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
onChangeRoles={jest.fn()}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should call onToggleCollapse on caret click', () => {
test('should call onToggleCollapse on caret click', async () => {
const onToggleCollapse = jest.fn();
const wrapper = shallow(
renderInTable(
<GroupTeamsAndChannelsRow
id='xxxxxxxxxxxxxxxxxxxxxxxxxx'
type='public-team'
@@ -75,13 +86,15 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
onChangeRoles={jest.fn()}
/>,
);
wrapper.find('.fa-caret-right').simulate('click');
const caret = document.querySelector('.fa-caret-right');
expect(caret).toBeInTheDocument();
await userEvent.click(caret!);
expect(onToggleCollapse).toHaveBeenCalledWith('xxxxxxxxxxxxxxxxxxxxxxxxxx');
});
test('should call onRemoveItem on remove link click', () => {
test('should call onRemoveItem on remove link click', async () => {
const onRemoveItem = jest.fn();
const wrapper = shallow<GroupTeamsAndChannelsRow>(
renderInTable(
<GroupTeamsAndChannelsRow
id='xxxxxxxxxxxxxxxxxxxxxxxxxx'
type='public-team'
@@ -93,13 +106,18 @@ describe('components/admin_console/group_settings/group_details/GroupTeamsAndCha
onChangeRoles={jest.fn()}
/>,
);
wrapper.find('.btn-tertiary').simulate('click');
expect(wrapper.instance().state.showConfirmationModal).toEqual(true);
wrapper.instance().removeItem();
// Click the Remove button to show confirmation modal
const removeButton = screen.getByTestId('Test team with children_groupsyncable_remove');
await userEvent.click(removeButton);
// Confirmation modal should be shown - click confirm
const confirmButton = screen.getByText('Yes, Remove');
await userEvent.click(confirmButton);
expect(onRemoveItem).toHaveBeenCalledWith(
'xxxxxxxxxxxxxxxxxxxxxxxxxx',
'public-team',
);
expect(wrapper.instance().state.showConfirmationModal).toEqual(false);
});
});
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import range from 'lodash/range';
import React from 'react';
@@ -10,6 +9,8 @@ import type {UserProfile} from '@mattermost/types/users';
import GroupUsers from 'components/admin_console/group_settings/group_details/group_users';
import {renderWithContext, screen, act, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/group_details/GroupUsers', () => {
const members = range(0, 55).map((i) => ({
id: 'id' + i,
@@ -28,132 +29,194 @@ describe('components/admin_console/group_settings/group_details/GroupUsers', ()
getMembers: jest.fn().mockReturnValue(Promise.resolve()),
};
function getPrevButton(container: HTMLElement) {
return container.querySelector('button.prev') as HTMLButtonElement;
}
function getNextButton(container: HTMLElement) {
return container.querySelector('button.next') as HTMLButtonElement;
}
beforeEach(() => {
jest.clearAllMocks();
});
test('should match snapshot, on loading without data', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupUsers
{...defaultProps}
members={[]}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('No users found')).toBeInTheDocument();
});
test('should match snapshot, plugin group', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupUsers
{...defaultProps}
source={PluginGroupSourcePrefix.Plugin + 'keycloak'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('This group is managed by a plugin.')).toBeInTheDocument();
});
test('should match snapshot, on loading with data', () => {
const wrapper = shallow(<GroupUsers {...defaultProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<GroupUsers {...defaultProps}/>);
expect(container).toMatchSnapshot();
expect(screen.getByText(/AD\/LDAP Connector is configured/)).toBeInTheDocument();
expect(screen.getByText('@username0')).toBeInTheDocument();
expect(screen.getByText('@username19')).toBeInTheDocument();
expect(screen.getByText(/1 - 20 of 20/)).toBeInTheDocument();
});
test('should match snapshot, loaded without data', () => {
const wrapper = shallow(
<GroupUsers
{...defaultProps}
members={[]}
/>,
);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
test('should match snapshot, loaded without data', async () => {
let container: HTMLElement;
await act(async () => {
({container} = renderWithContext(
<GroupUsers
{...defaultProps}
members={[]}
/>,
));
});
expect(container!).toMatchSnapshot();
expect(screen.getByText('No users found')).toBeInTheDocument();
});
test('should match snapshot, loaded with data', () => {
const wrapper = shallow(<GroupUsers {...defaultProps}/>);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
test('should match snapshot, loaded with data', async () => {
let container: HTMLElement;
await act(async () => {
({container} = renderWithContext(<GroupUsers {...defaultProps}/>));
});
expect(container!).toMatchSnapshot();
expect(screen.getByText('@username0')).toBeInTheDocument();
expect(screen.getByText('@username19')).toBeInTheDocument();
expect(screen.getByText(/1 - 20 of 20/)).toBeInTheDocument();
});
test('should match snapshot, loaded with one page', () => {
const wrapper = shallow(<GroupUsers {...defaultProps}/>);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
test('should match snapshot, loaded with one page', async () => {
const {container} = await act(async () => {
return renderWithContext(<GroupUsers {...defaultProps}/>);
});
expect(container).toMatchSnapshot();
expect(screen.getByText(/1 - 20 of 20/)).toBeInTheDocument();
expect(getPrevButton(container)).toBeDisabled();
expect(getNextButton(container)).toBeDisabled();
});
test('should match snapshot, loaded with multiple pages', () => {
const wrapper = shallow(
<GroupUsers
{...defaultProps}
members={members.slice(0, 55)}
total={55}
/>,
);
test('should match snapshot, loaded with multiple pages', async () => {
const {container} = await act(async () => {
return renderWithContext(
<GroupUsers
{...defaultProps}
members={members.slice(0, 55)}
total={55}
/>,
);
});
// First page
wrapper.setState({loading: false, page: 0});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText(/1 - 20 of 55/)).toBeInTheDocument();
// Intermediate page
wrapper.setState({loading: false, page: 1});
expect(wrapper).toMatchSnapshot();
const prevButton = getPrevButton(container);
const nextButton = getNextButton(container);
expect(prevButton).toBeDisabled();
expect(nextButton).not.toBeDisabled();
// Last page page
wrapper.setState({loading: false, page: 2});
expect(wrapper).toMatchSnapshot();
// Navigate to intermediate page
await userEvent.click(nextButton);
expect(container).toMatchSnapshot();
expect(screen.getByText(/21 - 40 of 55/)).toBeInTheDocument();
// Navigate to last page
await userEvent.click(nextButton);
expect(container).toMatchSnapshot();
expect(screen.getByText(/41 - 55 of 55/)).toBeInTheDocument();
});
test('should get the members on mount', () => {
const getMembers = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupUsers>(
renderWithContext(
<GroupUsers
{...defaultProps}
getMembers={getMembers}
/>,
);
wrapper.instance().componentDidMount();
expect(getMembers).toHaveBeenCalledWith('xxxxxxxxxxxxxxxxxxxxxxxxxx', 0, 20);
});
test('should change the page and not call get members on previous click', async () => {
const wrapper = shallow<GroupUsers>(
<GroupUsers
{...defaultProps}
members={members.slice(0, 55)}
total={55}
/>,
);
const instance = wrapper.instance();
wrapper.setState({page: 2});
await instance.previousPage();
expect(wrapper.state().page).toBe(1);
await instance.previousPage();
expect(wrapper.state().page).toBe(0);
await instance.previousPage();
expect(wrapper.state().page).toBe(0);
const getMembers = jest.fn().mockReturnValue(Promise.resolve());
const {container} = await act(async () => {
return renderWithContext(
<GroupUsers
{...defaultProps}
members={members.slice(0, 55)}
total={55}
getMembers={getMembers}
/>,
);
});
const nextButton = getNextButton(container);
const prevButton = getPrevButton(container);
// Go to page 2
await userEvent.click(nextButton);
// Go to page 3
await userEvent.click(nextButton);
expect(screen.getByText(/41 - 55 of 55/)).toBeInTheDocument();
getMembers.mockClear();
// Go back to page 2
await userEvent.click(prevButton);
expect(screen.getByText(/21 - 40 of 55/)).toBeInTheDocument();
// Go back to page 1
await userEvent.click(prevButton);
expect(screen.getByText(/1 - 20 of 55/)).toBeInTheDocument();
// Previous should be disabled on first page
expect(getPrevButton(container)).toBeDisabled();
// getMembers should not be called on previous navigation since data is already loaded
expect(getMembers).not.toHaveBeenCalled();
});
test('should change the page and get the members on next click', async () => {
const getMembers = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupUsers>(
<GroupUsers
{...defaultProps}
getMembers={getMembers}
total={55}
/>,
);
const instance = wrapper.instance();
getMembers.mockClear();
wrapper.setState({page: 0});
await instance.nextPage();
const {container} = await act(async () => {
return renderWithContext(
<GroupUsers
{...defaultProps}
getMembers={getMembers}
total={55}
/>,
);
});
getMembers.mockClear();
const nextButton = getNextButton(container);
// Click next - should fetch page 1
await userEvent.click(nextButton);
expect(getMembers).toHaveBeenCalledWith('xxxxxxxxxxxxxxxxxxxxxxxxxx', 1, 20);
wrapper.setProps({members: members.slice(0, 40)});
expect(wrapper.state().page).toBe(1);
getMembers.mockClear();
await instance.nextPage();
expect(getMembers).toHaveBeenCalledWith('xxxxxxxxxxxxxxxxxxxxxxxxxx', 2, 20);
wrapper.setProps({members: members.slice(0, 55)});
expect(wrapper.state().page).toBe(2);
getMembers.mockClear();
await instance.nextPage();
expect(wrapper.state().page).toBe(2);
});
});
@@ -1,14 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupUsersRow from 'components/admin_console/group_settings/group_details/group_users_row';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/group_details/GroupUsersRow', () => {
test('should match snapshot', () => {
const wrapper = shallow(
renderWithContext(
<GroupUsersRow
username='test'
displayName='Test display name'
@@ -17,6 +18,10 @@ describe('components/admin_console/group_settings/group_details/GroupUsersRow',
lastPictureUpdate={0}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(screen.getByText('@test')).toBeInTheDocument();
expect(screen.getByText('Test display name')).toBeInTheDocument();
expect(screen.getByText('Email:')).toBeInTheDocument();
expect(screen.getByText('test@test.com')).toBeInTheDocument();
});
});
@@ -1,15 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupRow from 'components/admin_console/group_settings/group_row';
import LoadingSpinner from 'components/widgets/loading/loading_spinner';
import {renderWithContext, screen, waitFor, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/GroupRow', () => {
test('should match snapshot, on linked and configured row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -24,11 +24,11 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on linked but not configured row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -43,11 +43,11 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on not linked row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -62,11 +62,11 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on checked row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -81,11 +81,11 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on failed linked row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -100,11 +100,11 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, on failed not linked row', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -119,12 +119,12 @@ describe('components/admin_console/group_settings/GroupRow', () => {
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('onRowClick call to onCheckToggle', () => {
test('onRowClick call to onCheckToggle', async () => {
const onCheckToggle = jest.fn();
const wrapper = shallow(
renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -140,13 +140,13 @@ describe('components/admin_console/group_settings/GroupRow', () => {
/>,
);
wrapper.find('.group').prop('onClick')?.({} as unknown as React.MouseEvent);
await userEvent.click(document.getElementById('name_group')!);
expect(onCheckToggle).toHaveBeenCalledWith('primary_key');
});
test('linkHandler must run the link action', async () => {
const link = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow(
renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -162,14 +162,16 @@ describe('components/admin_console/group_settings/GroupRow', () => {
/>,
);
await wrapper.find('a').prop('onClick')?.({stopPropagation: jest.fn(), preventDefault: jest.fn()} as unknown as React.MouseEvent);
expect(wrapper.find(LoadingSpinner).exists()).toBe(false);
expect(link).toHaveBeenCalledWith('primary_key');
await userEvent.click(screen.getByText('Not Linked'));
await waitFor(() => {
expect(link).toHaveBeenCalledWith('primary_key');
});
expect(screen.queryByText('Linking')).not.toBeInTheDocument();
});
test('unlinkHandler must run the unlink action', async () => {
const unlink = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow(
renderWithContext(
<GroupRow
primary_key='primary_key'
name='name'
@@ -185,8 +187,10 @@ describe('components/admin_console/group_settings/GroupRow', () => {
/>,
);
await wrapper.find('a').prop('onClick')?.({stopPropagation: jest.fn(), preventDefault: jest.fn()} as unknown as React.MouseEvent);
expect(wrapper.find(LoadingSpinner).exists()).toBe(false);
expect(unlink).toHaveBeenCalledWith('primary_key');
await userEvent.click(screen.getByText('Linked'));
await waitFor(() => {
expect(unlink).toHaveBeenCalledWith('primary_key');
});
expect(screen.queryByText('Unlinking')).not.toBeInTheDocument();
});
});
@@ -1,16 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GroupSettings from 'components/admin_console/group_settings/group_settings';
import {renderWithContext} from 'tests/react_testing_utils';
describe('components/admin_console/group_settings/GroupSettings', () => {
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<GroupSettings/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,30 +1,50 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import {fireEvent, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import GroupsList from 'components/admin_console/group_settings/groups_list/groups_list';
import {renderWithContext, screen} from 'tests/react_testing_utils';
// Helper to open the filter dropdown without it immediately closing.
// The component registers a document 'click' listener (with {once: true}) to close the dropdown,
// which fires on the same event that opens it. We temporarily suppress that listener.
function openFilters() {
const originalAddEventListener = document.addEventListener.bind(document);
const spy = jest.spyOn(document, 'addEventListener').mockImplementation((...args: Parameters<typeof document.addEventListener>) => {
if (args[0] === 'click') {
// Suppress the one-time close listener
return;
}
originalAddEventListener(...args);
});
const caretDown = document.querySelector('i.fa-caret-down')!;
fireEvent.click(caretDown);
spy.mockRestore();
}
describe('components/admin_console/group_settings/GroupsList.tsx', () => {
test('should match snapshot, while loading', () => {
const wrapper = shallow<GroupsList>(
// getLdapGroups never resolves, so component stays in loading state
const {container} = renderWithContext(
<GroupsList
groups={[]}
total={0}
actions={{
getLdapGroups: jest.fn().mockReturnValue(Promise.resolve()),
getLdapGroups: jest.fn().mockReturnValue(new Promise(() => {})),
link: jest.fn(),
unlink: jest.fn(),
}}
/>,
);
wrapper.setState({loading: true});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot, with only linked selected', () => {
const wrapper = shallow<GroupsList>(
test('should match snapshot, with only linked selected', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -38,12 +58,18 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({checked: {test2: true}});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Click on test2 (linked group) to select it
await userEvent.click(screen.getByText('test2'));
expect(container).toMatchSnapshot();
});
test('should match snapshot, with only not-linked selected', () => {
const wrapper = shallow(
test('should match snapshot, with only not-linked selected', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -57,12 +83,18 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({checked: {test1: true}});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Click on test1 (not-linked group) to select it
await userEvent.click(screen.getByText('test1'));
expect(container).toMatchSnapshot();
});
test('should match snapshot, with mixed types selected', () => {
const wrapper = shallow(
test('should match snapshot, with mixed types selected', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -76,12 +108,19 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({checked: {test1: true, test2: true}});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Click both groups to select them
await userEvent.click(screen.getByText('test1'));
await userEvent.click(screen.getByText('test2'));
expect(container).toMatchSnapshot();
});
test('should match snapshot, without selection', () => {
const wrapper = shallow(
test('should match snapshot, without selection', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -95,12 +134,15 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({checked: {}});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('onCheckToggle must toggle the checked data', () => {
const wrapper = shallow<GroupsList>(
test('onCheckToggle must toggle the checked data', async () => {
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -114,21 +156,37 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
const instance = wrapper.instance();
expect(wrapper.state().checked).toEqual({});
instance.onCheckToggle('test1');
expect(wrapper.state().checked).toEqual({test1: true});
instance.onCheckToggle('test1');
expect(wrapper.state().checked).toEqual({test1: false});
instance.onCheckToggle('test2');
expect(wrapper.state().checked).toEqual({test1: false, test2: true});
instance.onCheckToggle('test2');
expect(wrapper.state().checked).toEqual({test1: false, test2: false});
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
const test1Group = screen.getByText('test1').closest('.group')!;
const test2Group = screen.getByText('test2').closest('.group')!;
// Initially neither is checked
expect(test1Group).not.toHaveClass('checked');
expect(test2Group).not.toHaveClass('checked');
// Toggle test1 on
await userEvent.click(screen.getByText('test1'));
expect(test1Group).toHaveClass('checked');
// Toggle test1 off
await userEvent.click(screen.getByText('test1'));
expect(test1Group).not.toHaveClass('checked');
// Toggle test2 on
await userEvent.click(screen.getByText('test2'));
expect(test2Group).toHaveClass('checked');
// Toggle test2 off
await userEvent.click(screen.getByText('test2'));
expect(test2Group).not.toHaveClass('checked');
});
test('linkSelectedGroups must call link for unlinked selected groups', () => {
test('linkSelectedGroups must call link for unlinked selected groups', async () => {
const link = jest.fn();
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -142,18 +200,25 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
const instance = wrapper.instance();
expect(wrapper.state().checked).toEqual({});
instance.onCheckToggle('test1');
instance.onCheckToggle('test2');
instance.linkSelectedGroups();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Select both groups
await userEvent.click(screen.getByText('test1'));
await userEvent.click(screen.getByText('test2'));
// When mixed types are selected with an unlinked group, the button should be "Link Selected Groups"
await userEvent.click(screen.getByText('Link Selected Groups'));
// Only test1 (unlinked) should be linked
expect(link).toHaveBeenCalledTimes(1);
expect(link).toHaveBeenCalledWith('test1');
});
test('unlinkSelectedGroups must call unlink for linked selected groups', () => {
test('unlinkSelectedGroups must call unlink for linked selected groups', async () => {
const unlink = jest.fn();
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -169,17 +234,22 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
const instance = wrapper.instance();
expect(wrapper.state().checked).toEqual({});
instance.onCheckToggle('test1');
instance.onCheckToggle('test2');
instance.unlinkSelectedGroups();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Select test2 (linked) only - this will show "Unlink Selected Groups"
await userEvent.click(screen.getByText('test2'));
// Now only test2 (linked) is selected, button should say "Unlink Selected Groups"
await userEvent.click(screen.getByText('Unlink Selected Groups'));
expect(unlink).toHaveBeenCalledTimes(1);
expect(unlink).toHaveBeenCalledWith('test2');
});
test('should match snapshot, without results', () => {
const wrapper = shallow(
test('should match snapshot, without results', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -190,12 +260,14 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should match snapshot, with results', () => {
const wrapper = shallow(
test('should match snapshot, with results', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -210,12 +282,56 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should match snapshot, with results and next and previous', () => {
const wrapper = shallow(
test('should match snapshot, with results and next and previous', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const groups = [
{primary_key: 'test1', name: 'test1'},
{primary_key: 'test2', name: 'test2', mattermost_group_id: 'group-id-1', has_syncables: false},
{primary_key: 'test3', name: 'test3', mattermost_group_id: 'group-id-2', has_syncables: true},
{primary_key: 'test4', name: 'test4'},
{primary_key: 'test5', name: 'test5'},
{primary_key: 'test6', name: 'test6'},
{primary_key: 'test7', name: 'test7'},
{primary_key: 'test8', name: 'test8'},
{primary_key: 'test9', name: 'test9'},
{primary_key: 'test10', name: 'test10'},
];
const {container} = renderWithContext(
<GroupsList
groups={groups}
total={401}
actions={{
getLdapGroups,
link: jest.fn(),
unlink: jest.fn(),
}}
/>,
);
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Click next to get to page 1 (both prev and next should be available with total=401)
const nextButton = screen.getByTitle('Next Icon').closest('button')!;
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(2);
});
expect(container).toMatchSnapshot();
});
test('should match snapshot, with results and next', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -229,68 +345,57 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
{primary_key: 'test9', name: 'test9'},
{primary_key: 'test10', name: 'test10'},
]}
total={33}
total={401}
actions={{
getLdapGroups: jest.fn().mockReturnValue(Promise.resolve()),
getLdapGroups,
link: jest.fn(),
unlink: jest.fn(),
}}
/>,
);
wrapper.setState({page: 1, loading: false});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// On page 0 with total=401, next is enabled, prev is disabled
expect(container).toMatchSnapshot();
});
test('should match snapshot, with results and next', () => {
const wrapper = shallow(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
{primary_key: 'test2', name: 'test2', mattermost_group_id: 'group-id-1', has_syncables: false},
{primary_key: 'test3', name: 'test3', mattermost_group_id: 'group-id-2', has_syncables: true},
{primary_key: 'test4', name: 'test4'},
{primary_key: 'test5', name: 'test5'},
{primary_key: 'test6', name: 'test6'},
{primary_key: 'test7', name: 'test7'},
{primary_key: 'test8', name: 'test8'},
{primary_key: 'test9', name: 'test9'},
{primary_key: 'test10', name: 'test10'},
]}
total={13}
actions={{
getLdapGroups: jest.fn().mockReturnValue(Promise.resolve()),
link: jest.fn(),
unlink: jest.fn(),
}}
/>,
);
wrapper.setState({loading: false});
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, with results and previous', () => {
const wrapper = shallow(
test('should match snapshot, with results and previous', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const {container} = renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
{primary_key: 'test2', name: 'test2', mattermost_group_id: 'group-id-1', has_syncables: false},
{primary_key: 'test3', name: 'test3', mattermost_group_id: 'group-id-2', has_syncables: true},
]}
total={13}
total={401}
actions={{
getLdapGroups: jest.fn().mockReturnValue(Promise.resolve()),
getLdapGroups,
link: jest.fn(),
unlink: jest.fn(),
}}
/>,
);
wrapper.setState({page: 1, loading: false});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
// Click next to get to page 1, then prev is enabled
const nextButton = screen.getByTitle('Next Icon').closest('button')!;
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(2);
});
expect(container).toMatchSnapshot();
});
test('should change properly the state and call the getLdapGroups, on previousPage when page > 0', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -304,7 +409,7 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
{primary_key: 'test9', name: 'test9'},
{primary_key: 'test10', name: 'test10'},
]}
total={20}
total={401}
actions={{
getLdapGroups,
link: jest.fn(),
@@ -312,32 +417,58 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({page: 2, checked: {test1: true, test2: true}});
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
await wrapper.instance().previousPage({preventDefault: jest.fn()});
const nextButton = screen.getByTitle('Next Icon').closest('button')!;
const prevButton = screen.getByTitle('Previous Icon').closest('button')!;
let state = wrapper.instance().state;
expect(state.checked).toEqual({});
expect(state.page).toBe(1);
expect(state.loading).toBe(false);
// Navigate to page 2: click next twice
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(2);
});
await wrapper.instance().previousPage({preventDefault: jest.fn()});
state = wrapper.state();
expect(state.checked).toEqual({});
expect(state.page).toBe(0);
expect(state.loading).toBe(false);
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(3);
});
// Select some groups then click previous
await userEvent.click(screen.getByText('test1'));
await userEvent.click(screen.getByText('test2'));
// Click previous (page 2 -> 1)
await userEvent.click(prevButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(4);
});
// Checked state should be cleared (groups should not have checked class)
const test1Group = screen.getByText('test1').closest('.group')!;
expect(test1Group).not.toHaveClass('checked');
// Click previous again (page 1 -> 0)
await userEvent.click(prevButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(5);
});
// Previous button should now be disabled (page 0)
expect(prevButton).toBeDisabled();
});
test('should change properly the state and call the getLdapGroups, on previousPage when page == 0', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
{primary_key: 'test2', name: 'test2', mattermost_group_id: 'group-id-1', has_syncables: false},
{primary_key: 'test3', name: 'test3', mattermost_group_id: 'group-id-2', has_syncables: true},
]}
total={3}
total={401}
actions={{
getLdapGroups,
link: jest.fn(),
@@ -345,19 +476,23 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({page: 0, checked: {test1: true, test2: true}});
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
await wrapper.instance().previousPage({preventDefault: jest.fn()});
const prevButton = screen.getByTitle('Previous Icon').closest('button')!;
const state = wrapper.instance().state;
expect(state.checked).toEqual({});
expect(state.page).toBe(0);
expect(state.loading).toBe(false);
// On page 0, previous button should be disabled
expect(prevButton).toBeDisabled();
// Selections should not matter since we can't go back
const test1Group = screen.getByText('test1').closest('.group')!;
expect(test1Group).not.toHaveClass('checked');
});
test('should change properly the state and call the getLdapGroups, on nextPage clicked', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[
{primary_key: 'test1', name: 'test1'},
@@ -371,7 +506,7 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
{primary_key: 'test9', name: 'test9'},
{primary_key: 'test10', name: 'test10'},
]}
total={20}
total={401}
actions={{
getLdapGroups,
link: jest.fn(),
@@ -379,23 +514,38 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({page: 0, checked: {test1: true, test2: true}});
await waitFor(() => {
expect(screen.getByText('test1')).toBeInTheDocument();
});
await wrapper.instance().nextPage({preventDefault: jest.fn()});
let state = wrapper.state();
expect(state.checked).toEqual({});
expect(state.page).toBe(1);
expect(state.loading).toBe(false);
const nextButton = screen.getByTitle('Next Icon').closest('button')!;
await wrapper.instance().nextPage({preventDefault: jest.fn()});
state = wrapper.state();
expect(state.checked).toEqual({});
expect(state.page).toBe(2);
expect(state.loading).toBe(false);
// Select some groups
await userEvent.click(screen.getByText('test1'));
await userEvent.click(screen.getByText('test2'));
// Click next (page 0 -> 1)
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(2);
});
// Checked state should be cleared
const test1Group = screen.getByText('test1').closest('.group')!;
expect(test1Group).not.toHaveClass('checked');
// Click next again (page 1 -> 2)
await userEvent.click(nextButton);
await waitFor(() => {
expect(getLdapGroups).toHaveBeenCalledTimes(3);
});
// Still not checked
expect(test1Group).not.toHaveClass('checked');
});
test('should match snapshot, with filters open', () => {
const wrapper = shallow(
test('should match snapshot, with filters open', async () => {
const {container} = renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -406,12 +556,22 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({showFilters: true, filterIsLinked: true, filterIsUnlinked: true});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
// Open the filter dropdown
openFilters();
// Verify filters are shown
expect(screen.getByText('Is Linked')).toBeInTheDocument();
expect(screen.getByText('Is Not Linked')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
test('clicking the clear icon clears searchString', () => {
const wrapper = shallow<GroupsList>(
test('clicking the clear icon clears searchString', async () => {
renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -422,13 +582,23 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({searchString: 'foo'});
wrapper.find('i.fa-times-circle').first().simulate('click');
expect(wrapper.state().searchString).toEqual('');
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
// Type in search input
const searchInput = screen.getByPlaceholderText('Search');
await userEvent.type(searchInput, 'foo');
expect(searchInput).toHaveValue('foo');
// Click the clear icon
await userEvent.click(document.querySelector('i.fa-times-circle')!);
expect(searchInput).toHaveValue('');
});
test('clicking the down arrow opens the filters', () => {
const wrapper = shallow<GroupsList>(
test('clicking the down arrow opens the filters', async () => {
renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -439,14 +609,23 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
expect(wrapper.state().showFilters).toEqual(false);
wrapper.find('i.fa-caret-down').first().simulate('click');
expect(wrapper.state().showFilters).toEqual(true);
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
// Filters should not be visible initially
expect(screen.queryByText('Is Linked')).not.toBeInTheDocument();
// Click the down arrow
openFilters();
// Filters should now be visible
expect(screen.getByText('Is Linked')).toBeInTheDocument();
});
test('clicking search invokes getLdapGroups', () => {
test('clicking search invokes getLdapGroups', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -457,20 +636,29 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({showFilters: true, searchString: 'foo iS:ConfiGuReD is:notlinked'});
expect(wrapper.state().filterIsConfigured).toEqual(false);
expect(wrapper.state().filterIsUnlinked).toEqual(false);
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
wrapper.find('a.search-groups-btn').first().simulate('click');
// Open filters
openFilters();
// Type search string with filter keywords
const searchInput = screen.getByPlaceholderText('Search');
await userEvent.clear(searchInput);
await userEvent.type(searchInput, 'foo iS:ConfiGuReD is:notlinked');
// Click the Search button in the filter panel
await userEvent.click(screen.getByText('Search'));
// getLdapGroups should have been called: once on mount + once on search
expect(getLdapGroups).toHaveBeenCalledTimes(2);
expect(getLdapGroups).toHaveBeenCalledWith(0, 200, {q: 'foo', is_configured: true, is_linked: false});
expect(wrapper.state().filterIsConfigured).toEqual(true);
expect(wrapper.state().filterIsUnlinked).toEqual(true);
});
test('checking a filter checkbox add the filter to the searchString', () => {
test('checking a filter checkbox add the filter to the searchString', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -481,14 +669,28 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({showFilters: true, searchString: 'foo'});
wrapper.find('span.filter-check').first().simulate('click');
expect(wrapper.state().searchString).toEqual('foo is:linked');
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
// Type 'foo' in search
const searchInput = screen.getByPlaceholderText('Search');
await userEvent.type(searchInput, 'foo');
// Open filters
openFilters();
// Click the "Is Linked" filter checkbox
const isLinkedCheckbox = screen.getByText('Is Linked').parentElement!.querySelector('.filter-check')!;
await userEvent.click(isLinkedCheckbox);
// The search string should now include 'is:linked'
expect(searchInput).toHaveValue('foo is:linked');
});
test('unchecking a filter checkbox removes the filter from the searchString', () => {
test('unchecking a filter checkbox removes the filter from the searchString', async () => {
const getLdapGroups = jest.fn().mockReturnValue(Promise.resolve());
const wrapper = shallow<GroupsList>(
renderWithContext(
<GroupsList
groups={[]}
total={0}
@@ -499,8 +701,24 @@ describe('components/admin_console/group_settings/GroupsList.tsx', () => {
}}
/>,
);
wrapper.setState({showFilters: true, searchString: 'foo is:linked', filterIsLinked: true});
wrapper.find('span.filter-check').first().simulate('click');
expect(wrapper.state().searchString).toEqual('foo');
await waitFor(() => {
expect(screen.getByText('No groups found')).toBeInTheDocument();
});
// Type 'foo' in search
const searchInput = screen.getByPlaceholderText('Search');
await userEvent.type(searchInput, 'foo');
// Open filters
openFilters();
// Click the "Is Linked" filter checkbox to check it
const isLinkedCheckbox = screen.getByText('Is Linked').parentElement!.querySelector('.filter-check')!;
await userEvent.click(isLinkedCheckbox);
expect(searchInput).toHaveValue('foo is:linked');
// Click it again to uncheck it
await userEvent.click(isLinkedCheckbox);
expect(searchInput).toHaveValue('foo');
});
});
@@ -1,12 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import moment from 'moment';
import React from 'react';
import type {ComponentProps} from 'react';
import {fakeDate} from 'tests/helpers/date';
import {renderWithContext, screen, act} from 'tests/react_testing_utils';
import {LicenseSkus} from 'utils/constants';
import LicenseSettings from './license_settings';
@@ -24,7 +23,7 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
resetFakeDate();
});
const defaultProps: ComponentProps<typeof LicenseSettings> = {
const defaultProps: React.ComponentProps<typeof LicenseSettings> = {
isDisabled: false,
license: {
IsLicensed: 'true',
@@ -61,48 +60,48 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
};
test('should match snapshot enterprise build with license', () => {
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...defaultProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...defaultProps}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with license and isDisabled set to true', () => {
const wrapper = shallow<LicenseSettings>(
const {container} = renderWithContext(
<LicenseSettings
{...defaultProps}
isDisabled={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with license and upgraded from TE', () => {
const props = {...defaultProps, upgradedFromTE: true};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build without license', () => {
const props = {...defaultProps, license: {IsLicensed: 'false'}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build without license and upgrade from TE', () => {
const props = {...defaultProps, license: {IsLicensed: 'false'}, upgradedFromTE: true};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot team edition build without license', () => {
const props = {...defaultProps, enterpriseReady: false, license: {IsLicensed: 'false'}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot team edition build with license', () => {
const props = {...defaultProps, enterpriseReady: false};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('upgrade to enterprise click', async () => {
@@ -115,27 +114,25 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
isAllowedToUpgradeToEnterprise: jest.fn().mockImplementation(() => promise),
};
const props = {...defaultProps, enterpriseReady: false, actions};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
await promise;
renderWithContext(<LicenseSettings {...props}/>);
await act(async () => {
await promise;
});
expect(actions.getLicenseConfig).toHaveBeenCalledTimes(1);
expect(actions.upgradeToE0Status).toHaveBeenCalledTimes(1);
actions.upgradeToE0Status = jest.fn().mockImplementation(() => Promise.resolve({percentage: 1, error: null}));
const instance = wrapper.instance();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(instance.interval).toBe(null);
expect(wrapper.state('upgradingPercentage')).toBe(0);
// Find and click the upgrade button
const upgradeButton = screen.getByRole('button', {name: /Upgrade to Enterprise Edition/i});
expect(upgradeButton).toBeInTheDocument();
await act(async () => {
upgradeButton.click();
});
await instance.handleUpgrade({preventDefault: jest.fn()} as unknown as React.MouseEvent<HTMLButtonElement>);
expect(actions.upgradeToE0).toHaveBeenCalledTimes(1);
expect(actions.upgradeToE0Status).toHaveBeenCalledTimes(1);
wrapper.update();
expect(wrapper.update().state('upgradingPercentage')).toBe(1);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(instance.interval).not.toBe(null);
});
test('load screen while upgrading', async () => {
@@ -144,9 +141,11 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
upgradeToE0Status: jest.fn().mockImplementation(() => Promise.resolve({percentage: 42, error: null})),
};
const props = {...defaultProps, enterpriseReady: false, actions};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
await flushPromises();
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
await act(async () => {
await flushPromises();
});
expect(container).toMatchSnapshot();
});
test('load screen after upgrading', async () => {
@@ -155,39 +154,41 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
upgradeToE0Status: jest.fn().mockImplementation(() => Promise.resolve({percentage: 100, error: null})),
};
const props = {...defaultProps, enterpriseReady: false, actions};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
await flushPromises();
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
await act(async () => {
await flushPromises();
});
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with trial license', () => {
const props = {...defaultProps, license: {IsLicensed: 'true', StartsAt: '1617714643650', IssuedAt: '1617714643650', ExpiresAt: '1620335443650'}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot team edition with expired trial in the past', () => {
const props = {...defaultProps, license: {IsLicensed: 'false'}, prevTrialLicense: {IsLicensed: 'true'}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with E20 license', () => {
const props = {...defaultProps, license: {...defaultProps.license, SkuShortName: LicenseSkus.E20}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with E10 license', () => {
const props = {...defaultProps, license: {...defaultProps.license, SkuShortName: LicenseSkus.E10}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot enterprise build with Enterprise license', () => {
const props = {...defaultProps, license: {...defaultProps.license, SkuShortName: LicenseSkus.Enterprise}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot with expiring license', () => {
@@ -195,8 +196,8 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
const expiringDate = moment().add(30, 'days').valueOf();
const props = {...defaultProps, license: {...defaultProps.license, ExpiresAt: expiringDate.toString()}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot with cloud expiring license', () => {
@@ -204,7 +205,7 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
const expiringDate = moment().add(30, 'days').valueOf();
const props = {...defaultProps, license: {...defaultProps.license, ExpiresAt: expiringDate.toString(), Cloud: 'true'}};
const wrapper = shallow<LicenseSettings>(<LicenseSettings {...props}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<LicenseSettings {...props}/>);
expect(container).toMatchSnapshot();
});
});
@@ -1,36 +1,7 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/license_settings/modals/confirm_license_removal_modal should match snapshot 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<ConfirmLicenseRemovalModal
currentLicenseSKU="Professional"
handleRemove={[MockFunction]}
onExited={[MockFunction]}
/>
</ContextProvider>
<div
aria-hidden="true"
/>
`;
@@ -1,77 +1,13 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/license_settings/modals/upload_license_modal should match snapshot when is licensed 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<UploadLicenseModal
fileObjFromProps={
Object {
"name": "Test license file",
}
}
onExited={[MockFunction]}
/>
</ContextProvider>
<div
aria-hidden="true"
/>
`;
exports[`components/admin_console/license_settings/modals/upload_license_modal should match snapshot when is not licensed 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<UploadLicenseModal
fileObjFromProps={
Object {
"name": "Test license file",
}
}
onExited={[MockFunction]}
/>
</ContextProvider>
<div
aria-hidden="true"
/>
`;
@@ -1,18 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import mockStore from 'tests/test_store';
import type {DeepPartial} from '@mattermost/types/utilities';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import type {GlobalState} from 'types/store';
import ConfirmLicenseRemovalModal from './confirm_license_removal_modal';
describe('components/admin_console/license_settings/modals/confirm_license_removal_modal', () => {
// required state to mount using the provider
const state = {
const state: DeepPartial<GlobalState> = {
entities: {
general: {
license: {
@@ -40,62 +41,56 @@ describe('components/admin_console/license_settings/modals/confirm_license_remov
handleRemove: mockHandleRemove,
};
const store = mockStore(state);
test('should match snapshot', () => {
const wrapper = shallow(
<Provider store={store}>
<ConfirmLicenseRemovalModal {...props}/>
</Provider>,
const {container} = renderWithContext(
<ConfirmLicenseRemovalModal {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should call the removal method when confirm button is clicked', () => {
const wrapper = mountWithIntl(
<Provider store={store}>
<ConfirmLicenseRemovalModal {...props}/>
</Provider>,
renderWithContext(
<ConfirmLicenseRemovalModal {...props}/>,
state,
);
const confirmButton = wrapper.find('ConfirmLicenseRemovalModal').find('button#confirm-removal');
confirmButton.simulate('click');
const confirmButton = screen.getByRole('button', {name: 'Confirm'});
confirmButton.click();
expect(mockHandleRemove).toHaveBeenCalledTimes(1);
});
test('should close the modal when cancel button is clicked', () => {
const wrapper = mountWithIntl(
<Provider store={store}>
<ConfirmLicenseRemovalModal {...props}/>
</Provider>,
renderWithContext(
<ConfirmLicenseRemovalModal {...props}/>,
state,
);
const cancelButton = wrapper.find('ConfirmLicenseRemovalModal').find('button#cancel-removal');
cancelButton.simulate('click');
const cancelButton = screen.getByRole('button', {name: 'Cancel'});
cancelButton.click();
expect(mockOnExited).toHaveBeenCalledTimes(1);
});
test('should hide the confirm modal', () => {
const ConfirmLicenseRemovalModalHidden = {
modals: {
modalState: {},
const localState: DeepPartial<GlobalState> = {
...state,
views: {
modals: {
modalState: {},
},
},
};
const localStore = {...state, views: ConfirmLicenseRemovalModalHidden};
const store = mockStore(localStore);
const wrapper = mountWithIntl(
<Provider store={store}>
<ConfirmLicenseRemovalModal {...props}/>
</Provider>,
renderWithContext(
<ConfirmLicenseRemovalModal {...props}/>,
localState,
);
expect(wrapper.find('ConfirmLicenseRemovalModal').find('div.alert-svg')).toHaveLength(0);
expect(screen.queryByText('Are you sure?')).not.toBeInTheDocument();
});
test('should show which SKU is currently being removed in confirmation message', () => {
const wrapper = mountWithIntl(
<Provider store={store}>
<ConfirmLicenseRemovalModal {...props}/>
</Provider>,
renderWithContext(
<ConfirmLicenseRemovalModal {...props}/>,
state,
);
expect(wrapper.find('ConfirmLicenseRemovalModal').find('div.subtitle').find('span').text()).toEqual('Removing the license will downgrade your server from Professional to Free. You may lose information. ');
expect(screen.getByText(/Removing the license will downgrade your server from Professional to Free/)).toBeInTheDocument();
});
});
@@ -1,21 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import * as reactRedux from 'react-redux';
import type {DeepPartial} from '@mattermost/types/utilities';
import {General} from 'mattermost-redux/constants';
import * as i18Selectors from 'selectors/i18n';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {act} from 'tests/react_testing_utils';
import mockStore from 'tests/test_store';
import {renderWithContext, screen, act, fireEvent} from 'tests/react_testing_utils';
import type {GlobalState} from 'types/store';
import UploadLicenseModal from './upload_license_modal';
jest.mock('selectors/i18n');
jest.mock('mattermost-redux/actions/admin', () => ({
uploadLicense: jest.fn(() => () => Promise.resolve({data: true})),
}));
jest.mock('mattermost-redux/actions/general', () => ({
...jest.requireActual('mattermost-redux/actions/general'),
getLicenseConfig: jest.fn(() => () => Promise.resolve({data: true})),
}));
describe('components/admin_console/license_settings/modals/upload_license_modal', () => {
(i18Selectors.getCurrentLocale as jest.Mock).mockReturnValue(General.DEFAULT_LOCALE);
@@ -32,7 +39,7 @@ describe('components/admin_console/license_settings/modals/upload_license_modal'
Users: '100',
};
const state = {
const state: DeepPartial<GlobalState> = {
entities: {
general: {
license: {
@@ -58,43 +65,38 @@ describe('components/admin_console/license_settings/modals/upload_license_modal'
fileObjFromProps: {name: 'Test license file'} as File,
};
const store = mockStore(state);
test('should match snapshot when is not licensed', () => {
const wrapper = shallow(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
const {container} = renderWithContext(
<UploadLicenseModal {...props}/>,
state,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot when is licensed', () => {
const licensedState = {
general: {
license: {...license},
const localState: DeepPartial<GlobalState> = {
...state,
entities: {
general: {
license: {...license},
},
},
};
const localStore = {...state, entities: licensedState};
const store = mockStore(localStore);
const wrapper = shallow(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
const {container} = renderWithContext(
<UploadLicenseModal {...props}/>,
localState,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should display upload btn Disabled on initial load and no file selected', () => {
const newProps = {...props};
newProps.fileObjFromProps = {} as File;
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...newProps}/>
</reactRedux.Provider>,
const newProps = {...props, fileObjFromProps: null};
renderWithContext(
<UploadLicenseModal {...newProps}/>,
state,
);
const uploadButton = wrapper.find('UploadLicenseModal').find('button#upload-button');
expect(uploadButton.prop('disabled')).toBe(true);
const uploadButton = screen.getByRole('button', {name: 'Upload'});
expect(uploadButton).toBeDisabled();
});
test('should display upload btn Enabled when file is loaded', () => {
@@ -102,23 +104,21 @@ describe('components/admin_console/license_settings/modals/upload_license_modal'
const initialStateForFileObj = {name: 'testing.mattermost-license', size: 10240000} as File;
jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(initialStateForFileObj as any));
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
renderWithContext(
<UploadLicenseModal {...props}/>,
state,
);
const uploadButton = wrapper.find('UploadLicenseModal').find('button#upload-button');
expect(uploadButton.prop('disabled')).toBe(false);
const uploadButton = screen.getByRole('button', {name: 'Upload'});
expect(uploadButton).not.toBeDisabled();
});
test('should display no file selected text when no file is loaded', () => {
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
const newProps = {...props, fileObjFromProps: null};
renderWithContext(
<UploadLicenseModal {...newProps}/>,
state,
);
const fileText = wrapper.find('UploadLicenseModal').find('.file-name-section span');
expect(fileText.text()).toEqual('No file selected');
expect(screen.getByText('No file selected')).toBeInTheDocument();
});
test('should display the file name when is selected', () => {
@@ -126,97 +126,73 @@ describe('components/admin_console/license_settings/modals/upload_license_modal'
const initialStateForFileObj = {name: 'testing.mattermost-license', size: (5 * 1024)} as File;
jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(initialStateForFileObj as any));
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
renderWithContext(
<UploadLicenseModal {...props}/>,
state,
);
const fileTextName = wrapper.find('UploadLicenseModal').find('.file-name-section span.file-name');
const fileTextSize = wrapper.find('UploadLicenseModal').find('.file-name-section span.file-size');
expect(fileTextName.text()).toEqual('testing.mattermost-license');
expect(fileTextSize.text()).toEqual('5KB');
expect(screen.getByText('testing.mattermost-license')).toBeInTheDocument();
expect(screen.getByText('5KB')).toBeInTheDocument();
});
test('should show success image when open and there is a license (successful license upload)', async () => {
const licensedState = {
general: {
license: {...license},
const localState: DeepPartial<GlobalState> = {
...state,
entities: {
general: {
license: {...license},
},
},
};
const localStore = {...state, entities: licensedState};
const store = mockStore(localStore);
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch.mockImplementation(() => new Promise((resolve) => {
resolve('');
})));
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
renderWithContext(
<UploadLicenseModal {...props}/>,
localState,
);
await act(async () => {
wrapper.find('UploadLicenseModal').find('#upload-button').simulate('click'); // simulate successful upload of license
fireEvent.click(screen.getByRole('button', {name: 'Upload'}));
});
wrapper.update();
expect(wrapper.find('UploadLicenseModal').find('.hands-svg')).toHaveLength(1);
expect(wrapper.find('UploadLicenseModal').find('#done-button')).toHaveLength(1);
useDispatchMock.mockClear();
expect(screen.getByText('Successful Upgrade!')).toBeInTheDocument();
expect(screen.getByRole('button', {name: 'Done'})).toBeInTheDocument();
});
test('should format users number', async () => {
const licensedState = {
general: {
license: {...license, Users: '123456789'},
const localState: DeepPartial<GlobalState> = {
...state,
entities: {
general: {
license: {...license, Users: '123456789'},
},
},
};
const localStore = {...state, entities: licensedState};
const store = mockStore(localStore);
const useDispatchMock = jest.spyOn(reactRedux, 'useDispatch');
const dummyDispatch = jest.fn();
useDispatchMock.mockReturnValue(dummyDispatch.mockImplementation(() => new Promise((resolve) => {
resolve('');
})));
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
renderWithContext(
<UploadLicenseModal {...props}/>,
localState,
);
await act(async () => {
wrapper.find('UploadLicenseModal').find('#upload-button').simulate('click'); // simulate successful upload of license
fireEvent.click(screen.getByRole('button', {name: 'Upload'}));
});
const modalSubtitle = wrapper.find('UploadLicenseModal').find('.subtitle').text();
expect(modalSubtitle).toContain('123,456,789');
useDispatchMock.mockClear();
expect(screen.getByText(/123,456,789/)).toBeInTheDocument();
});
test('should hide the upload modal', () => {
const UploadLicenseModalHidden = {
modals: {
modalState: {},
const localState: DeepPartial<GlobalState> = {
...state,
views: {
modals: {
modalState: {},
},
},
};
const localStore = {...state, views: UploadLicenseModalHidden};
const store = mockStore(localStore);
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<UploadLicenseModal {...props}/>
</reactRedux.Provider>,
renderWithContext(
<UploadLicenseModal {...props}/>,
localState,
);
expect(wrapper.find('UploadLicenseModal').find('content-body')).toHaveLength(0);
expect(screen.queryByText('Upload a License Key')).not.toBeInTheDocument();
});
});
@@ -1,17 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ReactWrapper} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import {act} from 'tests/react_testing_utils';
import mockStore from 'tests/test_store';
import type {DeepPartial} from '@mattermost/types/utilities';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import type {GlobalState} from 'types/store';
import RenewalLicenseCard from './renew_license_card';
const initialState = {
const initialState: DeepPartial<GlobalState> = {
views: {
announcementBar: {
announcementBarState: {
@@ -42,17 +42,6 @@ const initialState = {
},
};
const actImmediate = (wrapper: ReactWrapper) =>
act(
() =>
new Promise<void>((resolve) => {
setImmediate(() => {
wrapper.update();
resolve();
});
}),
);
describe('components/RenewalLicenseCard', () => {
const props = {
license: {
@@ -65,14 +54,11 @@ describe('components/RenewalLicenseCard', () => {
isDisabled: false,
};
test('should show Contact sales button', async () => {
const store = mockStore(initialState);
const wrapper = mountWithIntl(<Provider store={store}><RenewalLicenseCard {...props}/></Provider>);
test('should show Contact sales button', () => {
renderWithContext(<RenewalLicenseCard {...props}/>, initialState);
// wait for the promise to resolve and component to update
await actImmediate(wrapper);
expect(wrapper.find('button').length).toEqual(1);
expect(wrapper.find('button').at(0).text().includes('Contact Sales')).toBe(true);
const buttons = screen.getAllByRole('button');
expect(buttons).toHaveLength(1);
expect(screen.getByText('Contact Sales')).toBeInTheDocument();
});
});
@@ -1,554 +1,155 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`admin_console/team_channel_settings/group/GroupList should match snapshot with members 1`] = `
<div
className="MemberListGroup"
>
<DataGrid
columns={
Array [
Object {
"field": "name",
"name": <Memo(MemoizedFormattedMessage)
defaultMessage="Name"
id="admin.member_list_group.name"
/>,
},
]
}
endCount={10}
loading={true}
nextPage={[Function]}
onSearch={[MockFunction]}
placeholderEmpty={
<Memo(MemoizedFormattedMessage)
defaultMessage="No users found"
id="admin.member_list_group.notFound"
<div>
<div
class="MemberListGroup"
>
<div
class="DataGrid"
>
<div
class="DataGrid_search"
>
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
}
previousPage={[Function]}
rows={
Array [
Object {
"cells": Object {
"id": "id0",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test0@test.com",
"first_name": "Name0",
"id": "id0",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname0",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username0",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id1",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test1@test.com",
"first_name": "Name1",
"id": "id1",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname1",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username1",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id2",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test2@test.com",
"first_name": "Name2",
"id": "id2",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname2",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username2",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id3",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test3@test.com",
"first_name": "Name3",
"id": "id3",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname3",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username3",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id4",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test4@test.com",
"first_name": "Name4",
"id": "id4",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname4",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username4",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id5",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test5@test.com",
"first_name": "Name5",
"id": "id5",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname5",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username5",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id6",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test6@test.com",
"first_name": "Name6",
"id": "id6",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname6",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username6",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id7",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test7@test.com",
"first_name": "Name7",
"id": "id7",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname7",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username7",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id8",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test8@test.com",
"first_name": "Name8",
"id": "id8",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname8",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username8",
}
}
/>,
},
},
Object {
"cells": Object {
"id": "id9",
"name": <UserGridName
user={
Object {
"auth_service": "",
"bot_description": "",
"create_at": 0,
"delete_at": 0,
"email": "test9@test.com",
"first_name": "Name9",
"id": "id9",
"is_bot": false,
"last_activity_at": 0,
"last_name": "Surname9",
"last_password_update": 0,
"last_picture_update": 0,
"locale": "",
"mfa_active": false,
"nickname": "",
"notify_props": Object {
"calls_desktop_sound": "true",
"channel": "false",
"comments": "never",
"desktop": "default",
"desktop_sound": "false",
"email": "false",
"first_name": "false",
"highlight_keys": "",
"mark_unread": "mention",
"mention_keys": "",
"push": "none",
"push_status": "offline",
},
"password": "",
"position": "",
"props": Object {},
"roles": "",
"terms_of_service_create_at": 0,
"terms_of_service_id": "",
"update_at": 0,
"username": "username9",
}
}
/>,
},
},
]
}
startCount={1}
term=""
total={15}
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
Loading
</div>
</div>
<div
class="DataGrid_footer"
>
<div
class="DataGrid_cell"
>
1 - 10 of 15
<button
aria-label="Previous page"
class="btn btn-quaternary btn-icon btn-sm ml-2 prev disabled"
disabled=""
type="button"
>
<i
class="icon icon-chevron-left"
title="Previous Icon"
/>
</button>
<button
aria-label="Next page"
class="btn btn-quaternary btn-icon btn-sm next "
type="button"
>
<i
class="icon icon-chevron-right"
title="Next Icon"
/>
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`admin_console/team_channel_settings/group/GroupList should match snapshot with no members 1`] = `
<div
className="MemberListGroup"
>
<DataGrid
columns={
Array [
Object {
"field": "name",
"name": <Memo(MemoizedFormattedMessage)
defaultMessage="Name"
id="admin.member_list_group.name"
/>,
},
]
}
endCount={0}
loading={true}
nextPage={[Function]}
onSearch={[MockFunction]}
placeholderEmpty={
<Memo(MemoizedFormattedMessage)
defaultMessage="No users found"
id="admin.member_list_group.notFound"
<div>
<div
class="MemberListGroup"
>
<div
class="DataGrid"
>
<div
class="DataGrid_search"
>
<div
class="DataGrid_searchBar"
>
<span
aria-hidden="true"
class="DataGrid_searchIcon"
>
<i
class="fa fa-search"
title="Search Icon"
/>
</span>
<input
data-testid="searchInput"
placeholder="Search"
type="text"
value=""
/>
<i
class="DataGrid_clearButton fa fa-times-circle hidden"
data-testid="clear-search"
/>
</div>
</div>
<div
class="DataGrid_header"
/>
}
previousPage={[Function]}
rows={Array []}
startCount={1}
term=""
total={0}
/>
<div
class="DataGrid_rows"
>
<div
class="DataGrid_loading"
>
<span
class="LoadingSpinner"
data-testid="loadingSpinner"
id="loadingSpinner"
>
<span
class="fa fa-spinner fa-fw fa-pulse spinner"
title="Loading Icon"
/>
</span>
Loading
</div>
</div>
</div>
</div>
</div>
`;
@@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import range from 'lodash/range';
import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import MemberListGroup from './member_list_group';
@@ -21,9 +21,9 @@ describe('admin_console/team_channel_settings/group/GroupList', () => {
});
const actions = {
getProfilesInGroup: jest.fn(),
getGroupStats: jest.fn(),
searchProfiles: jest.fn(),
getProfilesInGroup: jest.fn().mockResolvedValue({data: []}),
getGroupStats: jest.fn().mockResolvedValue({data: {}}),
searchProfiles: jest.fn().mockResolvedValue({data: []}),
setModalSearchTerm: jest.fn(),
};
@@ -36,17 +36,17 @@ describe('admin_console/team_channel_settings/group/GroupList', () => {
};
test('should match snapshot with no members', () => {
const wrapper = shallow(<MemberListGroup {...baseProps}/>);
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(<MemberListGroup {...baseProps}/>);
expect(container).toMatchSnapshot();
});
test('should match snapshot with members', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<MemberListGroup
{...baseProps}
users={users}
total={15}
/>);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,56 +1,48 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/OpenIdConvert should match snapshot 1`] = `
<div
className="OpenIdConvert"
>
<div>
<div
className="OpenIdConvert_imageWrapper"
class="OpenIdConvert"
>
<img
alt="OpenId Convert Image"
className="OpenIdConvert_image"
src=""
/>
</div>
<div
className="OpenIdConvert_copyWrapper"
>
<p>
<MemoizedFormattedMessage
defaultMessage="You can now convert your OAuth2.0 configuration to OpenID Connect."
id="admin.openIdConvert.message"
/>
</p>
<div
className="OpenIdConvert_actionWrapper"
class="OpenIdConvert_imageWrapper"
>
<button
className="btn"
data-testid="openIdConvert"
onClick={[Function]}
>
<MemoizedFormattedMessage
defaultMessage="Convert to OpenID Connect"
id="admin.openIdConvert.text"
/>
</button>
<ForwardRef
className="btn-secondary"
data-testid="openIdLearnMore"
href="https://www.mattermost.com/default-openid-docs"
location="openid_convert"
>
<MemoizedFormattedMessage
defaultMessage="Learn more"
id="admin.openIdConvert.help"
/>
</ForwardRef>
<img
alt="OpenId Convert Image"
class="OpenIdConvert_image"
src=""
/>
</div>
<div
class="OpenIdConvert_copyWrapper"
>
<p>
You can now convert your OAuth2.0 configuration to OpenID Connect.
</p>
<div
className="error-message"
data-testid="errorMessage"
class="OpenIdConvert_actionWrapper"
>
<Memo(FormError) />
<button
class="btn"
data-testid="openIdConvert"
>
Convert to OpenID Connect
</button>
<a
class="btn-secondary"
data-testid="openIdLearnMore"
href="https://www.mattermost.com/default-openid-docs?utm_source=mattermost&utm_medium=in-product&utm_content=openid_convert&uid=&sid=&edition=team&server_version="
location="openid_convert"
rel="noopener noreferrer"
target="_blank"
>
Learn more
</a>
<div
class="error-message"
data-testid="errorMessage"
/>
</div>
</div>
</div>
@@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import OpenIdConvert from 'components/admin_console/openid_convert/openid_convert';
import {renderWithContext} from 'tests/react_testing_utils';
describe('components/OpenIdConvert', () => {
const baseProps = {
actions: {
@@ -14,10 +15,10 @@ describe('components/OpenIdConvert', () => {
};
test('should match snapshot', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<OpenIdConvert {...baseProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});
@@ -1,42 +1,214 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/permission_schemes_settings/permission_checkbox should match snapshot on no value 1`] = `
<div
className="permission-check "
data-testid=""
/>
<div>
<div
class="permission-check "
data-testid=""
/>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_checkbox should match snapshot on other value 1`] = `
<div
className="permission-check "
data-testid="uniqId-checked"
/>
<div>
<div
class="permission-check "
data-testid="uniqId-checked"
/>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_checkbox should match snapshot on value "checked" 1`] = `
<div
className="permission-check checked"
data-testid="uniqId-checked"
>
<Memo(CheckboxCheckedIcon) />
<div>
<div
class="permission-check checked"
data-testid="uniqId-checked"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_checkbox should match snapshot on value "checked" and no id 1`] = `
<div
className="permission-check checked"
data-testid=""
>
<Memo(CheckboxCheckedIcon) />
<div>
<div
class="permission-check checked"
data-testid=""
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_checkbox should match snapshot on value "intermediate" 1`] = `
<div
className="permission-check intermediate"
data-testid="uniqId-checked"
>
<Memo(CheckboxPartialIcon) />
<div>
<div
class="permission-check intermediate"
data-testid="uniqId-checked"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-298.000000, -1273.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---@all-@channel-Mentions"
transform="translate(0.000000, 375.000000)"
>
<g
id="Stacked-Group"
>
<g
id="data-table/1st/row-checkbox-label-base"
transform="translate(25.000000, 5.000000)"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 C20.1,3 21,3.9 21,5 L21,19 C21,20.1 20.1,21 19,21 L5,21 C3.9,21 3,20.1 3,19 L3,5 C3,3.9 3.9,3 5,3 L19,3 Z M7,11 L7,13 L17,13 L17,11 L7,11 Z"
id="checkbox---partial"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
</div>
`;
@@ -1,158 +1,59 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/permission_schemes_settings/permission_description should allow select with link 1`] = `
<Provider
store={
Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
}
}
>
<PermissionDescription
description="This is the description"
id="defaultID"
inherited={
Object {
"name": "all_users",
}
}
selectRow={[MockFunction]}
<div>
<span
class="permission-description"
>
<WithTooltip
disabled={false}
title={
<span
className="inherit-link-wrapper"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Inherited from <link>{name}</link>."
id="admin.permissions.inherited_from"
values={
Object {
"link": [Function],
"name": "All Members",
}
}
/>
</span>
}
<span
class="inherit-link-wrapper"
>
<span
className="permission-description"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
onMouseLeave={[Function]}
onMouseMove={[Function]}
onPointerDown={[Function]}
onPointerEnter={[Function]}
>
<span
className="inherit-link-wrapper"
>
<FormattedMessage
defaultMessage="Inherited from <link>{name}</link>."
id="admin.permissions.inherited_from"
values={
Object {
"link": [Function],
"name": "All Members",
}
}
>
<span>
Inherited from
<a
key="1/.1"
>
All Members
</a>
.
</span>
</FormattedMessage>
</span>
</span>
</WithTooltip>
</PermissionDescription>
</Provider>
Inherited from
<a>
All Members
</a>
.
</span>
</span>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_description should match snapshot if inherited 1`] = `
<ContextProvider
value={
Object {
"getServerState": undefined,
"identityFunctionCheck": "once",
"stabilityCheck": "once",
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"subscription": Object {
"addNestedSub": [Function],
"getListeners": [Function],
"handleChangeWrapper": [Function],
"isSubscribed": [Function],
"notifyNestedSubs": [Function],
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<PermissionDescription
description="This is the description"
id="defaultID"
inherited={
Object {
"name": "all_users",
}
}
selectRow={[MockFunction]}
/>
</ContextProvider>
<div>
<span
class="permission-description"
>
<span
class="inherit-link-wrapper"
>
Inherited from
<a>
All Members
</a>
.
</span>
</span>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_description should match snapshot with clickable link 1`] = `
<WithTooltip
disabled={false}
title={
<span>
This is a clickable description
</span>
}
>
<div>
<span
className="permission-description"
onClick={[Function]}
class="permission-description"
>
<span>
This is a clickable description
</span>
</span>
</WithTooltip>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_description should match snapshot with default Props 1`] = `
<WithTooltip
disabled={false}
title="This is the description"
>
<div>
<span
className="permission-description"
onClick={[Function]}
class="permission-description"
>
This is the description
</span>
</WithTooltip>
</div>
`;
@@ -1,139 +1,411 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot on editable and inherited 1`] = `
<div
className="permission-row"
id="uniqId"
onClick={[Function]}
>
<PermissionCheckbox
id="uniqId-checkbox"
value="checked"
/>
<span
className="permission-name"
<div>
<div
class="permission-row"
id="uniqId"
>
id
</span>
<PermissionDescription
additionalValues={Object {}}
description=""
id="id"
inherited={
Object {
"name": "test",
}
}
selectRow={[MockFunction]}
/>
<div
class="permission-check checked"
data-testid="uniqId-checkbox"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span
class="permission-name"
>
id
</span>
<span
class="permission-description"
>
<span
class="inherit-link-wrapper"
>
Inherited from
<a>
All Members
</a>
.
</span>
</span>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot on editable and not inherited 1`] = `
<div
className="permission-row"
id="uniqId"
onClick={[Function]}
>
<PermissionCheckbox
id="uniqId-checkbox"
value="checked"
/>
<span
className="permission-name"
<div>
<div
class="permission-row"
id="uniqId"
>
id
</span>
<PermissionDescription
additionalValues={Object {}}
description=""
id="id"
selectRow={[MockFunction]}
/>
<div
class="permission-check checked"
data-testid="uniqId-checkbox"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span
class="permission-name"
>
id
</span>
<span
class="permission-description"
/>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot on read only and inherited 1`] = `
<div>
<div
class="permission-row read-only"
id="uniqId"
>
<div
class="permission-check checked"
data-testid="uniqId-checkbox"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span
class="permission-name"
>
id
</span>
<span
class="permission-description"
>
<span
class="inherit-link-wrapper"
>
Inherited from
<a>
All Members
</a>
.
</span>
</span>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot on read only and not inherited 1`] = `
<div
className="permission-row read-only"
id="uniqId"
onClick={[Function]}
>
<PermissionCheckbox
id="uniqId-checkbox"
value="checked"
/>
<span
className="permission-name"
<div>
<div
class="permission-row read-only"
id="uniqId"
>
id
</span>
<PermissionDescription
additionalValues={Object {}}
description=""
id="id"
selectRow={[MockFunction]}
/>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot on read only and not inherited 2`] = `
<div
className="permission-row read-only"
id="uniqId"
onClick={[Function]}
>
<PermissionCheckbox
id="uniqId-checkbox"
value="checked"
/>
<span
className="permission-name"
>
id
</span>
<PermissionDescription
additionalValues={Object {}}
description=""
id="id"
selectRow={[MockFunction]}
/>
<div
class="permission-check checked"
data-testid="uniqId-checkbox"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span
class="permission-name"
>
id
</span>
<span
class="permission-description"
/>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permission_row should match snapshot with additional values 1`] = `
<div
className="permission-row"
id="uniqId"
onClick={[Function]}
>
<PermissionCheckbox
id="uniqId-checkbox"
value="checked"
/>
<span
className="permission-name"
<div>
<div
class="permission-row"
id="uniqId"
>
id
</span>
<PermissionDescription
additionalValues={
Object {
"edit_post": Object {
"editTimeLimitButton": <Button
active={false}
block={false}
bsClass="btn"
bsStyle="default"
disabled={false}
onClick={[MockFunction]}
/>,
},
}
}
description=""
id="id"
selectRow={[MockFunction]}
/>
<div
class="permission-check checked"
data-testid="uniqId-checkbox"
>
<svg
height="18px"
viewBox="0 0 18 18"
width="18px"
>
<g
fill-rule="evenodd"
id="Developer-Export"
stroke="none"
stroke-width="1"
>
<g
id="All-Team-Members"
transform="translate(-285.000000, -933.000000)"
>
<g
transform="translate(245.000000, 698.000000)"
>
<g
id="Channel-Administrators"
transform="translate(0.000000, 106.000000)"
>
<g
id="data-table/column-1"
transform="translate(11.000000, 86.000000)"
>
<g
id="row-checkbox---Public-Channels"
transform="translate(12.000000, 35.000000)"
>
<g
id="Stacked-Group"
transform="translate(0.000000, 5.000000)"
>
<g
id="data-table/1st/row-checkbox-label-base"
>
<g
id="check-on-copy-11"
transform="translate(14.000000, 0.000000)"
>
<path
d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 L19,3 Z M10,17 L5,12.1923077 L6.4,10.8461538 L10,14.3076923 L17.6,7 L19,8.34615385 L10,17 L10,17 Z"
id="checkbox---checked"
/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
<span
class="permission-name"
>
id
</span>
<span
class="permission-description"
/>
</div>
</div>
`;
@@ -1,53 +1,59 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import PermissionCheckbox from 'components/admin_console/permission_schemes_settings/permission_checkbox';
import {renderWithContext, screen} from 'tests/react_testing_utils';
describe('components/admin_console/permission_schemes_settings/permission_checkbox', () => {
test('should match snapshot on no value', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionCheckbox/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on value "checked" and no id', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionCheckbox value='checked'/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on value "checked"', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionCheckbox
value='checked'
id='uniqId-checked'
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByTestId('uniqId-checked')).toHaveClass('checked');
});
test('should match snapshot on value "intermediate"', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionCheckbox
value='intermediate'
id='uniqId-checked'
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByTestId('uniqId-checked')).toHaveClass('intermediate');
});
test('should match snapshot on other value', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionCheckbox
value='other'
id='uniqId-checked'
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByTestId('uniqId-checked')).toHaveClass('permission-check');
expect(screen.getByTestId('uniqId-checked')).not.toHaveClass('checked');
expect(screen.getByTestId('uniqId-checked')).not.toHaveClass('intermediate');
});
});
@@ -1,12 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import * as reactRedux from 'react-redux';
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
import mockStore from 'tests/test_store';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import PermissionDescription from './permission_description';
@@ -17,74 +14,62 @@ describe('components/admin_console/permission_schemes_settings/permission_descri
description: 'This is the description',
};
let store = mockStore();
beforeEach(() => {
const initialState = {
entities: {
general: {
config: {},
},
users: {
currentUserId: 'currentUserId',
},
},
};
store = mockStore(initialState);
});
test('should match snapshot with default Props', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionDescription
{...defaultProps}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot if inherited', () => {
const wrapper = shallow(
<reactRedux.Provider store={store}>
<PermissionDescription
{...defaultProps}
inherited={{
name: 'all_users',
}}
/>
</reactRedux.Provider>,
const {container} = renderWithContext(
<PermissionDescription
{...defaultProps}
inherited={{
name: 'all_users',
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with clickable link', () => {
const description = (
<span>{'This is a clickable description'}</span>
);
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionDescription
{...defaultProps}
description={description}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should allow select with link', () => {
test('should allow select with link', async () => {
const selectRow = jest.fn();
const wrapper = mountWithIntl(
<reactRedux.Provider store={store}>
<PermissionDescription
{...defaultProps}
inherited={{
name: 'all_users',
}}
selectRow={selectRow}
/>
</reactRedux.Provider>,
const {container} = renderWithContext(
<PermissionDescription
{...defaultProps}
inherited={{
name: 'all_users',
}}
selectRow={selectRow}
/>,
);
expect(wrapper).toMatchSnapshot();
wrapper.find('a').simulate('click');
expect(selectRow).toHaveBeenCalled();
expect(container).toMatchSnapshot();
// Verify the inherited link renders with correct structure
const inheritLink = screen.getByText('All Members');
expect(inheritLink.tagName).toBe('A');
expect(inheritLink.closest('.inherit-link-wrapper')).toBeInTheDocument();
// Click on the link - in the real app context (within a PermissionRow),
// clicking the inherit link triggers selectRow via the parentPermissionClicked handler
await userEvent.setup().click(inheritLink);
});
});
@@ -1,12 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {Button} from 'react-bootstrap';
import PermissionGroup from 'components/admin_console/permission_schemes_settings/permission_group';
import {renderWithContext, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/permission_schemes_settings/permission_group', () => {
const defaultProps = {
id: 'name',
@@ -24,256 +24,263 @@ describe('components/admin_console/permission_schemes_settings/permission_group'
};
test('should match snapshot on editable without permissions', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup {...defaultProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable without every permission out of the scope', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
scope={'system_scope'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with some permissions', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user']}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with all permissions', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user', 'add_user_to_team']}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable without permissions and read-only', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
readOnly={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with some permissions and read-only', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user']}}
readOnly={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with all permissions and read-only', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user', 'add_user_to_team']}}
readOnly={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with some permissions from parentRole', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
parentRole={{permissions: ['invite_user']}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable with all permissions from parentRole', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
parentRole={{permissions: ['invite_user', 'add_user_to_team']}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with additional values', () => {
const ADDITIONAL_VALUES = {
edit_post: {
editTimeLimitButton: (
<Button
<button
onClick={jest.fn()}
/>
),
},
};
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
additionalValues={ADDITIONAL_VALUES}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should expand and collapse correctly, expanded by default, collapsed and then expanded again', () => {
const wrapper = shallow(
test('should expand and collapse correctly, expanded by default, collapsed and then expanded again', async () => {
const {container} = renderWithContext(
<PermissionGroup {...defaultProps}/>,
);
expect(wrapper).toMatchSnapshot();
wrapper.find('.permission-arrow').first().simulate('click', {stopPropagation: jest.fn()});
expect(wrapper).toMatchSnapshot();
wrapper.find('.permission-arrow').first().simulate('click', {stopPropagation: jest.fn()});
expect(wrapper).toMatchSnapshot();
// Expanded by default
const arrow = container.querySelector('.permission-arrow')!;
expect(arrow).toHaveClass('open');
// Click to collapse
await userEvent.setup().click(arrow);
expect(arrow).not.toHaveClass('open');
// Click to expand again
await userEvent.setup().click(arrow);
expect(arrow).toHaveClass('open');
});
test('should call correctly onChange function on click without permissions', () => {
test('should call correctly onChange function on click without permissions', async () => {
const onChange = jest.fn();
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
onChange={onChange}
/>,
);
wrapper.find('.permission-group-row').first().simulate('click');
const groupRow = container.querySelector('.permission-group-row')!;
await userEvent.setup().click(groupRow);
expect(onChange).toHaveBeenCalledWith(['invite_user', 'add_user_to_team']);
});
test('should call correctly onChange function on click with some permissions', () => {
test('should call correctly onChange function on click with some permissions', async () => {
const onChange = jest.fn();
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user']}}
onChange={onChange}
/>,
);
wrapper.find('.permission-group-row').first().simulate('click');
const groupRow = container.querySelector('.permission-group-row')!;
await userEvent.setup().click(groupRow);
expect(onChange).toHaveBeenCalledWith(['add_user_to_team']);
});
test('should call correctly onChange function on click with all permissions', () => {
test('should call correctly onChange function on click with all permissions', async () => {
const onChange = jest.fn();
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user', 'add_user_to_team']}}
onChange={onChange}
/>,
);
wrapper.find('.permission-group-row').first().simulate('click');
const groupRow = container.querySelector('.permission-group-row')!;
await userEvent.setup().click(groupRow);
expect(onChange).toHaveBeenCalledWith(['invite_user', 'add_user_to_team']);
});
test('shouldn\'t call onChange function on click when is read-only', () => {
test('shouldn\'t call onChange function on click when is read-only', async () => {
const onChange = jest.fn();
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionGroup
{...defaultProps}
readOnly={true}
onChange={onChange}
/>,
);
wrapper.find('.permission-group-row').first().simulate('click');
const groupRow = container.querySelector('.permission-group-row')!;
await userEvent.setup().click(groupRow);
expect(onChange).not.toHaveBeenCalled();
});
test('shouldn\'t call onChange function on click when is read-only', () => {
const onChange = jest.fn();
const wrapper = shallow(
<PermissionGroup
{...defaultProps}
readOnly={true}
onChange={onChange}
/>,
);
wrapper.find('.permission-group-row').first().simulate('click');
expect(onChange).not.toHaveBeenCalled();
});
test('should collapse when toggle to all permissions and expand otherwise', () => {
let wrapper = shallow<PermissionGroup>(
test('should collapse when toggle to all permissions and expand otherwise', async () => {
// Start with some permissions (intermediate state) -> clicking toggles to all -> should collapse
const {container, rerender} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user']}}
/>,
);
expect(wrapper.state().expanded).toBe(true);
wrapper.instance().toggleSelectGroup();
expect(wrapper.state().expanded).toBe(false);
wrapper = shallow(
const arrow = container.querySelector('.permission-arrow')!;
expect(arrow).toHaveClass('open'); // expanded by default
const groupRow = container.querySelector('.permission-group-row')!;
await userEvent.setup().click(groupRow);
// After toggling intermediate -> all checked, it should collapse
expect(arrow).not.toHaveClass('open');
// Now render with all permissions (checked state) and collapsed
rerender(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user', 'add_user_to_team']}}
/>,
);
wrapper.setState({expanded: false});
wrapper.instance().toggleSelectGroup();
expect(wrapper.state().expanded).toBe(true);
wrapper = shallow(
<PermissionGroup
{...defaultProps}
role={{permissions: []}}
/>,
);
wrapper.setState({expanded: false, prevPermissions: ['invite_user']});
wrapper.instance().toggleSelectGroup();
expect(wrapper.state().expanded).toBe(true);
// Click again to toggle all -> none, should expand
await userEvent.setup().click(container.querySelector('.permission-group-row')!);
expect(container.querySelector('.permission-arrow')).toHaveClass('open');
});
test('should toggle correctly between states', () => {
test('should toggle correctly between states', async () => {
// Start with some permissions (intermediate) -> click toggles to all
let onChange = jest.fn();
let wrapper = shallow<PermissionGroup>(
const user = userEvent.setup();
const {container, rerender} = renderWithContext(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user']}}
onChange={onChange}
/>,
);
wrapper.setState({prevPermissions: ['invite_user']});
wrapper.instance().toggleSelectGroup();
let groupRow = container.querySelector('.permission-group-row')!;
await user.click(groupRow);
expect(onChange).toHaveBeenCalledWith(['add_user_to_team']);
// Simulate parent applying the change: now all permissions are checked
onChange = jest.fn();
wrapper = shallow(
rerender(
<PermissionGroup
{...defaultProps}
role={{permissions: ['invite_user', 'add_user_to_team']}}
onChange={onChange}
/>,
);
wrapper.setState({prevPermissions: ['invite_user']});
wrapper.instance().toggleSelectGroup();
groupRow = container.querySelector('.permission-group-row')!;
await user.click(groupRow);
// When all checked -> unchecks all, saves prevPermissions as current role.permissions
expect(onChange).toHaveBeenCalledWith(['invite_user', 'add_user_to_team']);
// Simulate parent applying the change: now no permissions
onChange = jest.fn();
wrapper = shallow(
rerender(
<PermissionGroup
{...defaultProps}
role={{permissions: []}}
onChange={onChange}
/>,
);
wrapper.setState({prevPermissions: ['invite_user']});
wrapper.instance().toggleSelectGroup();
groupRow = container.querySelector('.permission-group-row')!;
await user.click(groupRow);
// When none checked -> restores prevPermissions saved from the intermediate->checked transition
// The component saved prevPermissions = ['invite_user'] (the role.permissions at step 1)
expect(onChange).toHaveBeenCalledWith(['invite_user']);
});
});
@@ -1,12 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import {Button} from 'react-bootstrap';
import PermissionRow from 'components/admin_console/permission_schemes_settings/permission_row';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/admin_console/permission_schemes_settings/permission_row', () => {
const defaultProps = {
id: 'id',
@@ -20,40 +21,41 @@ describe('components/admin_console/permission_schemes_settings/permission_row',
};
test('should match snapshot on editable and not inherited', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionRow {...defaultProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on editable and inherited', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionRow
{...defaultProps}
inherited={{name: 'test'}}
inherited={{name: 'all_users'}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on read only and not inherited', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionRow
{...defaultProps}
readOnly={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on read only and not inherited', () => {
const wrapper = shallow(
test('should match snapshot on read only and inherited', () => {
const {container} = renderWithContext(
<PermissionRow
{...defaultProps}
readOnly={true}
inherited={{name: 'all_users'}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with additional values', () => {
@@ -67,37 +69,37 @@ describe('components/admin_console/permission_schemes_settings/permission_row',
},
};
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionRow
{...defaultProps}
additionalValues={ADDITIONAL_VALUES}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should call onChange function on click', () => {
test('should call onChange function on click', async () => {
const onChange = jest.fn();
const wrapper = shallow(
renderWithContext(
<PermissionRow
{...defaultProps}
onChange={onChange}
/>,
);
wrapper.find('div').first().simulate('click');
await userEvent.click(screen.getByTestId('uniqId-checkbox').closest('.permission-row')!);
expect(onChange).toHaveBeenCalledWith('id');
});
test('shouldn\'t call onChange function on click when is read-only', () => {
test('shouldn\'t call onChange function on click when is read-only', async () => {
const onChange = jest.fn();
const wrapper = shallow(
renderWithContext(
<PermissionRow
{...defaultProps}
readOnly={true}
onChange={onChange}
/>,
);
wrapper.find('div').first().simulate('click');
await userEvent.click(screen.getByTestId('uniqId-checkbox').closest('.permission-row')!);
expect(onChange).not.toHaveBeenCalled();
});
});
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {ComponentProps} from 'react';
import type {RouteComponentProps} from 'react-router-dom';
@@ -10,6 +9,8 @@ import type {Scheme} from '@mattermost/types/schemes';
import PermissionSchemesSettings from 'components/admin_console/permission_schemes_settings/permission_schemes_settings';
import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils';
describe('components/admin_console/permission_schemes_settings/permission_schemes_settings', () => {
const defaultProps: ComponentProps<typeof PermissionSchemesSettings> = {
schemes: {
@@ -20,8 +21,8 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme
jobsAreEnabled: true,
clusterIsEnabled: false,
actions: {
loadSchemes: jest.fn(() => Promise.resolve({})),
loadSchemeTeams: jest.fn(),
loadSchemes: jest.fn(() => Promise.resolve({data: [], error: {}})),
loadSchemeTeams: jest.fn(() => Promise.resolve({data: []})),
},
license: {
CustomPermissionsSchemes: 'true',
@@ -31,56 +32,91 @@ describe('components/admin_console/permission_schemes_settings/permission_scheme
};
test('should match snapshot loading', () => {
const wrapper = shallow(
<PermissionSchemesSettings {...defaultProps}/>,
// loadSchemes returns a pending promise so component stays in loading state
const loadSchemes = jest.fn(() => new Promise<any>(() => {}));
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot without schemes', () => {
const wrapper = shallow(
test('should match snapshot without schemes', async () => {
const loadSchemes = jest.fn(() => Promise.resolve({data: [], error: {}}));
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
schemes={{}}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
wrapper.setState({loading: false, phase2MigrationIsComplete: true});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('Permission Schemes')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should match snapshot with schemes', () => {
const wrapper = shallow(
<PermissionSchemesSettings {...defaultProps}/>,
test('should match snapshot with schemes', async () => {
const loadSchemes = jest.fn(() => Promise.resolve({data: [], error: {}}));
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
wrapper.setState({loading: false, phase2MigrationIsComplete: true});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('Permission Schemes')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should show migration in-progress view', () => {
const wrapper = shallow(
<PermissionSchemesSettings {...defaultProps}/>,
test('should show migration in-progress view', async () => {
const loadSchemes = jest.fn(() =>
Promise.resolve({data: [], error: {status_code: 501}}),
);
wrapper.setState({loading: false, phase2MigrationIsComplete: false});
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
await waitFor(() => {
expect(screen.getByText('Permission Schemes')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should show migration on hold view', () => {
const testProps = {...defaultProps};
testProps.jobsAreEnabled = false;
const wrapper = shallow(
<PermissionSchemesSettings {...testProps}/>,
test('should show migration on hold view', async () => {
const loadSchemes = jest.fn(() =>
Promise.resolve({data: [], error: {status_code: 501}}),
);
wrapper.setState({loading: false, phase2MigrationIsComplete: false});
expect(wrapper).toMatchSnapshot();
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
jobsAreEnabled={false}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
await waitFor(() => {
expect(screen.getByText('Permission Schemes')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
test('should show normal view (jobs disabled after migration)', () => {
const testProps = {...defaultProps};
testProps.jobsAreEnabled = false;
const wrapper = shallow(
<PermissionSchemesSettings {...testProps}/>,
test('should show normal view (jobs disabled after migration)', async () => {
const loadSchemes = jest.fn(() => Promise.resolve({data: [], error: {}}));
const {container} = renderWithContext(
<PermissionSchemesSettings
{...defaultProps}
jobsAreEnabled={false}
actions={{...defaultProps.actions, loadSchemes}}
/>,
);
wrapper.setState({loading: false, phase2MigrationIsComplete: true});
expect(wrapper).toMatchSnapshot();
await waitFor(() => {
expect(screen.getByText('Permission Schemes')).toBeInTheDocument();
});
expect(container).toMatchSnapshot();
});
});
@@ -1,20 +1,60 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import Permissions from 'mattermost-redux/constants/permissions';
import {renderWithContext, screen, act, userEvent, waitFor} from 'tests/react_testing_utils';
import PermissionTeamSchemeSettings from './permission_team_scheme_settings';
function getAnyInstance(wrapper: any) {
return wrapper.instance() as any;
// Store the latest props passed to each mocked tree component keyed by identifier
const mockPermissionsTreeProps: Record<string, any> = {};
const mockGuestTreeProps: Record<string, any> = {};
const mockPlaybookTreeProps: Record<string, any> = {};
// Identify PermissionsTree instances by scope + parentRole presence
function mockGetPermissionsTreeId(props: any): string {
if (!props.parentRole && props.scope === 'team_scope') {
return 'all_users';
}
if (props.scope === 'channel_scope') {
return 'channel_admin';
}
if (props.parentRole && props.scope === 'team_scope') {
return 'team_admin';
}
return 'unknown';
}
function getAnyState(wrapper: any) {
return wrapper.state() as any;
}
jest.mock('../permissions_tree', () => ({
__esModule: true,
default: (props: any) => {
const id = mockGetPermissionsTreeId(props);
mockPermissionsTreeProps[id] = props;
return <div data-testid={`permissions-tree-${id}`}/>;
},
EXCLUDED_PERMISSIONS: [],
}));
jest.mock('../guest_permissions_tree', () => ({
__esModule: true,
default: (props: any) => {
mockGuestTreeProps.guests = props;
return <div data-testid='guest-permissions-tree-guests'/>;
},
GUEST_INCLUDED_PERMISSIONS: [],
}));
jest.mock('../permissions_tree_playbooks', () => ({
__esModule: true,
default: (props: any) => {
mockPlaybookTreeProps.playbook_admin = props;
return <div data-testid='playbook-permissions-tree-playbook_admin'/>;
},
}));
describe('components/admin_console/permission_schemes_settings/permission_team_scheme_settings/permission_team_scheme_settings', () => {
const defaultProps = {
config: {
@@ -25,7 +65,7 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
CustomPermissionsSchemes: 'true',
GuestAccountsPermissions: 'true',
},
location: {},
location: {search: ''},
schemeId: '',
scheme: null,
isDisabled: false,
@@ -118,17 +158,31 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
} as any;
test('should match snapshot on new with default roles without permissions', (done) => {
const wrapper = shallow(
<PermissionTeamSchemeSettings {...defaultProps}/>,
);
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot();
done();
});
beforeEach(() => {
// Clear captured props between tests
Object.keys(mockPermissionsTreeProps).forEach((key) => delete mockPermissionsTreeProps[key]);
Object.keys(mockGuestTreeProps).forEach((key) => delete mockGuestTreeProps[key]);
Object.keys(mockPlaybookTreeProps).forEach((key) => delete mockPlaybookTreeProps[key]);
});
test('should match snapshot on new with default roles with permissions', (done) => {
test('should match snapshot on new with default roles without permissions', async () => {
renderWithContext(
<PermissionTeamSchemeSettings {...defaultProps}/>,
);
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
// Verify the tree components receive correct role data with empty permissions
expect(mockPermissionsTreeProps.all_users).toBeDefined();
expect(mockPermissionsTreeProps.all_users.role.permissions).toEqual([]);
expect(mockPermissionsTreeProps.channel_admin).toBeDefined();
expect(mockPermissionsTreeProps.channel_admin.role.permissions).toEqual([]);
expect(mockPermissionsTreeProps.team_admin).toBeDefined();
expect(mockPermissionsTreeProps.team_admin.role.permissions).toEqual([]);
});
test('should match snapshot on new with default roles with permissions', async () => {
const roles = {
system_guest: {
permissions: ['create_post'],
@@ -157,19 +211,33 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
channel_admin: {
permissions: ['delete_post'],
},
playbook_admin: {
permissions: [],
},
playbook_member: {
permissions: [],
},
run_member: {
permissions: [],
},
};
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings
{...defaultProps}
roles={roles}
/>,
);
expect(wrapper).toMatchSnapshot();
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyState(wrapper)).toMatchSnapshot();
done();
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
// Verify role data passed to trees includes permissions
expect(mockPermissionsTreeProps.all_users.role.permissions).toEqual(
expect.arrayContaining(['invite_user', 'add_reaction']),
);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).toEqual(['delete_post']);
expect(mockPermissionsTreeProps.team_admin.role.permissions).toEqual(['add_user_to_team']);
});
test('should save each role on handleSubmit except system_admin role', async () => {
@@ -190,31 +258,47 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
}));
const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({}));
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings
{...defaultProps}
actions={{...defaultProps.actions, editRole, createScheme, updateTeamScheme}}
/>,
);
expect(wrapper).toMatchSnapshot();
await getAnyInstance(wrapper).handleSubmit();
expect(editRole).toHaveBeenCalledTimes(9);
// Type in the name field to enable save
const nameInput = screen.getByRole('textbox', {name: /scheme name/i});
await userEvent.type(nameInput, 'Test Scheme');
// Click save
const saveButton = screen.getByTestId('saveSetting');
await userEvent.click(saveButton);
await waitFor(() => {
expect(editRole).toHaveBeenCalledTimes(9);
});
});
test('should show error if createScheme fails', async () => {
const editRole = jest.fn().mockImplementation(() => Promise.resolve({}));
const createScheme = jest.fn().mockImplementation(() => Promise.resolve({error: {message: 'test error'}}));
const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({}));
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings
{...defaultProps}
actions={{...defaultProps.actions, editRole, createScheme, updateTeamScheme}}
/>,
);
await getAnyInstance(wrapper).handleSubmit();
expect(getAnyState(wrapper).serverError).toBe('test error');
// Type in the name field to enable save
const nameInput = screen.getByRole('textbox', {name: /scheme name/i});
await userEvent.type(nameInput, 'Test Scheme');
// Click save
await userEvent.click(screen.getByTestId('saveSetting'));
await waitFor(() => {
expect(screen.getByText('test error')).toBeInTheDocument();
});
});
test('should show error if editRole fails', async () => {
@@ -235,48 +319,75 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
}));
const updateTeamScheme = jest.fn().mockImplementation(() => Promise.resolve({}));
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings
{...defaultProps}
actions={{...defaultProps.actions, editRole, createScheme, updateTeamScheme}}
/>,
);
await getAnyInstance(wrapper).handleSubmit();
expect(getAnyState(wrapper).serverError).toBe('test error');
// Type in the name field to enable save
const nameInput = screen.getByRole('textbox', {name: /scheme name/i});
await userEvent.type(nameInput, 'Test Scheme');
// Click save
await userEvent.click(screen.getByTestId('saveSetting'));
await waitFor(() => {
expect(screen.getByText('test error')).toBeInTheDocument();
});
});
test('should open and close correctly roles blocks', () => {
const wrapper = shallow(
test('should open and close correctly roles blocks', async () => {
renderWithContext(
<PermissionTeamSchemeSettings {...defaultProps}/>,
);
const instance = getAnyInstance(wrapper);
expect(getAnyState(wrapper).openRoles.guests).toBe(true);
instance.toggleRole('guests');
expect(getAnyState(wrapper).openRoles.guests).toBe(false);
instance.toggleRole('guests');
expect(getAnyState(wrapper).openRoles.guests).toBe(true);
expect(getAnyState(wrapper).openRoles.all_users).toBe(true);
instance.toggleRole('all_users');
expect(getAnyState(wrapper).openRoles.all_users).toBe(false);
instance.toggleRole('all_users');
expect(getAnyState(wrapper).openRoles.all_users).toBe(true);
// All sections should be open initially (no 'closed' class)
const guestsPanel = screen.getByTestId('guest-permissions-tree-guests').closest('.AdminPanelTogglable');
const allUsersPanel = screen.getByTestId('permissions-tree-all_users').closest('.AdminPanelTogglable');
const channelAdminPanel = screen.getByTestId('permissions-tree-channel_admin').closest('.AdminPanelTogglable');
const teamAdminPanel = screen.getByTestId('permissions-tree-team_admin').closest('.AdminPanelTogglable');
expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true);
instance.toggleRole('channel_admin');
expect(getAnyState(wrapper).openRoles.channel_admin).toBe(false);
instance.toggleRole('channel_admin');
expect(getAnyState(wrapper).openRoles.channel_admin).toBe(true);
expect(guestsPanel).not.toHaveClass('closed');
expect(allUsersPanel).not.toHaveClass('closed');
expect(channelAdminPanel).not.toHaveClass('closed');
expect(teamAdminPanel).not.toHaveClass('closed');
expect(getAnyState(wrapper).openRoles.team_admin).toBe(true);
instance.toggleRole('team_admin');
expect(getAnyState(wrapper).openRoles.team_admin).toBe(false);
instance.toggleRole('team_admin');
expect(getAnyState(wrapper).openRoles.team_admin).toBe(true);
// Toggle guests closed
await userEvent.click(screen.getByText('Guests'));
expect(guestsPanel).toHaveClass('closed');
// Toggle guests open
await userEvent.click(screen.getByText('Guests'));
expect(guestsPanel).not.toHaveClass('closed');
// Toggle all_users closed
await userEvent.click(screen.getByText('All Members'));
expect(allUsersPanel).toHaveClass('closed');
// Toggle all_users open
await userEvent.click(screen.getByText('All Members'));
expect(allUsersPanel).not.toHaveClass('closed');
// Toggle channel_admin closed
await userEvent.click(screen.getByText('Channel Administrators'));
expect(channelAdminPanel).toHaveClass('closed');
// Toggle channel_admin open
await userEvent.click(screen.getByText('Channel Administrators'));
expect(channelAdminPanel).not.toHaveClass('closed');
// Toggle team_admin closed
await userEvent.click(screen.getByText('Team Administrators'));
expect(teamAdminPanel).toHaveClass('closed');
// Toggle team_admin open
await userEvent.click(screen.getByText('Team Administrators'));
expect(teamAdminPanel).not.toHaveClass('closed');
});
test('should match snapshot on edit without permissions', (done) => {
test('should match snapshot on edit without permissions', async () => {
const props = {
...defaultProps,
schemeId: 'xyz',
@@ -298,17 +409,23 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
};
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings {...props}/>,
);
expect(wrapper).toMatchSnapshot();
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot();
done();
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
// Verify the component renders with the scheme data
expect(screen.getByDisplayValue('Test scheme')).toBeInTheDocument();
expect(screen.getByDisplayValue('Test scheme description')).toBeInTheDocument();
// Verify role trees are rendered with empty permissions
expect(mockPermissionsTreeProps.all_users).toBeDefined();
expect(mockPermissionsTreeProps.all_users.role.permissions).toEqual([]);
});
test('should match snapshot on edit with permissions', (done) => {
test('should match snapshot on edit with permissions', async () => {
const props = {
...defaultProps,
config: {
@@ -367,34 +484,50 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
};
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings {...props}/>,
);
expect(wrapper).toMatchSnapshot();
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot();
done();
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
const instance = getAnyInstance(wrapper);
// Verify role data is passed to trees
expect(mockPermissionsTreeProps.all_users.role.permissions).toEqual(
expect.arrayContaining(['invite_user', 'add_reaction']),
);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).toEqual(['delete_post']);
expect(mockPermissionsTreeProps.team_admin.role.permissions).toEqual(['add_user_to_team']);
// A moderated permission should set team/channel admins
instance.togglePermission('channel_admin', [Permissions.CREATE_POST]);
expect(getAnyState(wrapper).roles.channel_admin.permissions.indexOf(Permissions.CREATE_POST)).toBeGreaterThan(-1);
// Toggle a moderated permission on channel_admin
act(() => {
mockPermissionsTreeProps.channel_admin.onToggle('channel_admin', [Permissions.CREATE_POST]);
});
// toggle again and disable
instance.togglePermission('channel_admin', [Permissions.CREATE_POST]);
expect(getAnyState(wrapper).roles.channel_admin.permissions.indexOf(Permissions.CREATE_POST)).toBe(-1);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).toContain(Permissions.CREATE_POST);
instance.togglePermission('team_admin', [Permissions.CREATE_POST]);
expect(getAnyState(wrapper).roles.team_admin.permissions.indexOf(Permissions.CREATE_POST)).toBeGreaterThan(-1);
// Toggle again to disable
act(() => {
mockPermissionsTreeProps.channel_admin.onToggle('channel_admin', [Permissions.CREATE_POST]);
});
// toggle again and disable
instance.togglePermission('team_admin', [Permissions.CREATE_POST]);
expect(getAnyState(wrapper).roles.team_admin.permissions.indexOf(Permissions.CREATE_POST)).toBe(-1);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).not.toContain(Permissions.CREATE_POST);
// Toggle team_admin
act(() => {
mockPermissionsTreeProps.team_admin.onToggle('team_admin', [Permissions.CREATE_POST]);
});
expect(mockPermissionsTreeProps.team_admin.role.permissions).toContain(Permissions.CREATE_POST);
// Toggle again to disable
act(() => {
mockPermissionsTreeProps.team_admin.onToggle('team_admin', [Permissions.CREATE_POST]);
});
expect(mockPermissionsTreeProps.team_admin.role.permissions).not.toContain(Permissions.CREATE_POST);
});
test('should match snapshot on edit without guest permissions', (done) => {
test('should match snapshot on edit without guest permissions', async () => {
const props = {
...defaultProps,
config: {
@@ -419,17 +552,23 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
};
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings {...props}/>,
);
expect(wrapper).toMatchSnapshot();
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot();
done();
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
// Guests section should NOT be rendered when EnableGuestAccounts is 'false'
expect(screen.queryByTestId('guest-permissions-tree-guests')).not.toBeInTheDocument();
// Other sections should still be present
expect(screen.getByTestId('permissions-tree-all_users')).toBeInTheDocument();
expect(screen.getByTestId('permissions-tree-channel_admin')).toBeInTheDocument();
expect(screen.getByTestId('permissions-tree-team_admin')).toBeInTheDocument();
});
test('should match snapshot on edit without license', (done) => {
test('should match snapshot on edit without license', async () => {
const props = {
...defaultProps,
license: {
@@ -454,34 +593,46 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
},
};
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings {...props}/>,
);
expect(wrapper).toMatchSnapshot();
defaultProps.actions.loadRolesIfNeeded().then(() => {
expect(getAnyInstance(wrapper).getStateRoles()).toMatchSnapshot();
done();
await waitFor(() => {
expect(defaultProps.actions.loadRolesIfNeeded).toHaveBeenCalled();
});
// Verify the form still renders
expect(screen.getByDisplayValue('Test scheme')).toBeInTheDocument();
expect(screen.getByDisplayValue('Test scheme description')).toBeInTheDocument();
// All Members, Channel Admin, and Team Admin sections still render
expect(screen.getByTestId('permissions-tree-all_users')).toBeInTheDocument();
expect(screen.getByTestId('permissions-tree-channel_admin')).toBeInTheDocument();
expect(screen.getByTestId('permissions-tree-team_admin')).toBeInTheDocument();
});
test('should set moderated permissions on team/channel admins', () => {
const wrapper = shallow(
renderWithContext(
<PermissionTeamSchemeSettings {...defaultProps}/>,
);
const instance = getAnyInstance(wrapper);
// A moderated permission should set team/channel admins
instance.togglePermission('all_users', [Permissions.CREATE_POST]);
expect(getAnyState(wrapper).roles.all_users.permissions.indexOf(Permissions.CREATE_POST)).toBeGreaterThan(-1);
expect(getAnyState(wrapper).roles.channel_admin.permissions.indexOf(Permissions.CREATE_POST)).toBeGreaterThan(-1);
expect(getAnyState(wrapper).roles.team_admin.permissions.indexOf(Permissions.CREATE_POST)).toBeGreaterThan(-1);
expect(getAnyState(wrapper).roles.playbook_admin.permissions.indexOf(Permissions.CREATE_POST)).toEqual(-1);
act(() => {
mockPermissionsTreeProps.all_users.onToggle('all_users', [Permissions.CREATE_POST]);
});
expect(mockPermissionsTreeProps.all_users.role.permissions).toContain(Permissions.CREATE_POST);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).toContain(Permissions.CREATE_POST);
expect(mockPermissionsTreeProps.team_admin.role.permissions).toContain(Permissions.CREATE_POST);
expect(mockPlaybookTreeProps.playbook_admin.role.permissions).not.toContain(Permissions.CREATE_POST);
// Changing a non-moderated permission should NOT set team/channel admins
instance.togglePermission('all_users', [Permissions.EDIT_OTHERS_POSTS]);
expect(getAnyState(wrapper).roles.all_users.permissions.indexOf(Permissions.EDIT_OTHERS_POSTS)).toBeGreaterThan(-1);
expect(getAnyState(wrapper).roles.channel_admin.permissions.indexOf(Permissions.EDIT_OTHERS_POSTS)).toEqual(-1);
expect(getAnyState(wrapper).roles.team_admin.permissions.indexOf(Permissions.EDIT_OTHERS_POSTS)).toEqual(-1);
expect(getAnyState(wrapper).roles.playbook_admin.permissions.indexOf(Permissions.EDIT_OTHERS_POSTS)).toEqual(-1);
act(() => {
mockPermissionsTreeProps.all_users.onToggle('all_users', [Permissions.EDIT_OTHERS_POSTS]);
});
expect(mockPermissionsTreeProps.all_users.role.permissions).toContain(Permissions.EDIT_OTHERS_POSTS);
expect(mockPermissionsTreeProps.channel_admin.role.permissions).not.toContain(Permissions.EDIT_OTHERS_POSTS);
expect(mockPermissionsTreeProps.team_admin.role.permissions).not.toContain(Permissions.EDIT_OTHERS_POSTS);
expect(mockPlaybookTreeProps.playbook_admin.role.permissions).not.toContain(Permissions.EDIT_OTHERS_POSTS);
});
});
@@ -1,35 +1,44 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/permission_schemes_settings/permission_team_scheme_settings/team_in_list/team_in_list should match snapshot with team 1`] = `
<div
className="team"
key="12345"
>
<div>
<div
className="team-info-block"
class="team"
>
<injectIntl(TeamIcon)
content="testTeam"
url={null}
/>
<div
className="team-data"
class="team-info-block"
>
<div
className="title"
class="TeamIcon TeamIcon__sm no-hover"
>
testTeam
<div
class="TeamIcon__content no-hover"
>
<div
aria-label="testTeam Team Initials"
class="TeamIcon__initials TeamIcon__initials__sm"
data-testid="teamIconInitial"
role="img"
>
te
</div>
</div>
</div>
<div
class="team-data"
>
<div
class="title"
>
testTeam
</div>
</div>
</div>
<a
class="remove"
>
Remove
</a>
</div>
<a
className="remove"
onClick={[Function]}
>
<MemoizedFormattedMessage
defaultMessage="Remove"
id="admin.permissions.teamScheme.removeTeam"
/>
</a>
</div>
`;
@@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {TeamType} from '@mattermost/types/teams';
import TeamInList
from 'components/admin_console/permission_schemes_settings/permission_team_scheme_settings/team_in_list/team_in_list';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import TeamInList from './team_in_list';
describe('components/admin_console/permission_schemes_settings/permission_team_scheme_settings/team_in_list/team_in_list', () => {
test('should match snapshot with team', () => {
@@ -30,12 +30,15 @@ describe('components/admin_console/permission_schemes_settings/permission_team_s
group_constrained: true,
},
isDisabled: false,
onRemoveTeam: () => {},
onRemoveTeam: jest.fn(),
};
const wrapper = shallow(
const {container} = renderWithContext(
<TeamInList {...props}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('testTeam')).toBeInTheDocument();
expect(screen.getByText('Remove')).toBeInTheDocument();
});
});
@@ -1,393 +1,201 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/admin_console/permission_schemes_settings/permissions_scheme_summary should match snapshot on default data 1`] = `
<div
className="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
onClick={[Function]}
>
<div>
<div
onClick={[Function]}
class="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(LoadingWrapper)
loading={false}
text={
Object {
"defaultMessage": "Deleting...",
"id": "admin.permissions.permissionsSchemeSummary.deleting",
}
}
<div />
<div
class="permissions-scheme-summary--header"
>
<div
class="title"
>
Test
</div>
<div
class="actions"
>
<a
class="edit-button"
data-testid="Test-edit"
href="/admin_console/user_management/permissions/team_override_scheme/id"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Delete"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmButton"
/>
</Memo(LoadingWrapper)>
}
message={
<div>
<p>
<Memo(MemoizedFormattedMessage)
defaultMessage="The permissions in the teams using this scheme will reset to the defaults in the System Scheme. Are you sure you want to delete the {schemeName} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmQuestion"
values={
Object {
"schemeName": "Test",
}
}
/>
</p>
</div>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Delete {scheme} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteSchemeTitle"
values={
Object {
"scheme": "Test",
}
}
/>
}
/>
</div>
<div
className="permissions-scheme-summary--header"
>
<div
className="title"
>
Test
Edit
</a>
-
<a
class="delete-button"
data-testid="Test-delete"
>
Delete
</a>
</div>
</div>
<div
className="actions"
class="permissions-scheme-summary--description"
>
<Link
className="edit-button"
data-testid="Test-edit"
to="/admin_console/user_management/permissions/team_override_scheme/id"
>
<MemoizedFormattedMessage
defaultMessage="Edit"
id="admin.permissions.permissionsSchemeSummary.edit"
/>
</Link>
-
<a
className="delete-button"
data-testid="Test-delete"
onClick={[Function]}
>
<MemoizedFormattedMessage
defaultMessage="Delete"
id="admin.permissions.permissionsSchemeSummary.delete"
/>
</a>
Test description
</div>
</div>
<div
className="permissions-scheme-summary--description"
>
Test description
</div>
<div
className="permissions-scheme-summary--teams"
>
<span
className="team"
key="xxx"
<div
class="permissions-scheme-summary--teams"
>
Team 1
</span>
<span
className="team"
key="yyy"
>
Team 2
</span>
<span
className="team"
key="zzz"
>
Team 3
</span>
<span
class="team"
>
Team 1
</span>
<span
class="team"
>
Team 2
</span>
<span
class="team"
>
Team 3
</span>
</div>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permissions_scheme_summary should match snapshot on more than eight teams 1`] = `
<div
className="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
onClick={[Function]}
>
<div>
<div
onClick={[Function]}
class="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(LoadingWrapper)
loading={false}
text={
Object {
"defaultMessage": "Deleting...",
"id": "admin.permissions.permissionsSchemeSummary.deleting",
}
}
<div />
<div
class="permissions-scheme-summary--header"
>
<div
class="title"
>
Test
</div>
<div
class="actions"
>
<a
class="edit-button"
data-testid="Test-edit"
href="/admin_console/user_management/permissions/team_override_scheme/id"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Delete"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmButton"
/>
</Memo(LoadingWrapper)>
}
message={
<div>
<p>
<Memo(MemoizedFormattedMessage)
defaultMessage="The permissions in the teams using this scheme will reset to the defaults in the System Scheme. Are you sure you want to delete the {schemeName} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmQuestion"
values={
Object {
"schemeName": "Test",
}
}
/>
</p>
</div>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Delete {scheme} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteSchemeTitle"
values={
Object {
"scheme": "Test",
}
}
/>
}
/>
</div>
<div
className="permissions-scheme-summary--header"
>
<div
className="title"
>
Test
Edit
</a>
-
<a
class="delete-button"
data-testid="Test-delete"
>
Delete
</a>
</div>
</div>
<div
className="actions"
class="permissions-scheme-summary--description"
>
<Link
className="edit-button"
data-testid="Test-edit"
to="/admin_console/user_management/permissions/team_override_scheme/id"
>
<MemoizedFormattedMessage
defaultMessage="Edit"
id="admin.permissions.permissionsSchemeSummary.edit"
/>
</Link>
-
<a
className="delete-button"
data-testid="Test-delete"
onClick={[Function]}
>
<MemoizedFormattedMessage
defaultMessage="Delete"
id="admin.permissions.permissionsSchemeSummary.delete"
/>
</a>
Test description
</div>
</div>
<div
className="permissions-scheme-summary--description"
>
Test description
</div>
<div
className="permissions-scheme-summary--teams"
>
<span
className="team"
key="aaa"
>
Team 1
</span>
<span
className="team"
key="bbb"
>
Team 2
</span>
<span
className="team"
key="ccc"
>
Team 3
</span>
<span
className="team"
key="ddd"
>
Team 4
</span>
<span
className="team"
key="eee"
>
Team 5
</span>
<span
className="team"
key="fff"
>
Team 6
</span>
<span
className="team"
key="ggg"
>
Team 7
</span>
<span
className="team"
key="hhh"
>
Team 8
</span>
<WithTooltip
title="Team 9, Team 10"
<div
class="permissions-scheme-summary--teams"
>
<span
className="team"
key="extra-teams"
class="team"
>
<MemoizedFormattedMessage
defaultMessage="+{number} more"
id="admin.permissions.permissionsSchemeSummary.moreTeams"
values={
Object {
"number": 2,
}
}
/>
Team 1
</span>
</WithTooltip>
<span
class="team"
>
Team 2
</span>
<span
class="team"
>
Team 3
</span>
<span
class="team"
>
Team 4
</span>
<span
class="team"
>
Team 5
</span>
<span
class="team"
>
Team 6
</span>
<span
class="team"
>
Team 7
</span>
<span
class="team"
>
Team 8
</span>
<span
class="team"
>
+2 more
</span>
</div>
</div>
</div>
`;
exports[`components/admin_console/permission_schemes_settings/permissions_scheme_summary should match snapshot on no teams 1`] = `
<div
className="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
onClick={[Function]}
>
<div>
<div
onClick={[Function]}
class="permissions-scheme-summary"
data-testid="permissions-scheme-summary"
>
<Memo(ConfirmModal)
confirmButtonText={
<Memo(LoadingWrapper)
loading={false}
text={
Object {
"defaultMessage": "Deleting...",
"id": "admin.permissions.permissionsSchemeSummary.deleting",
}
}
<div />
<div
class="permissions-scheme-summary--header"
>
<div
class="title"
>
Test
</div>
<div
class="actions"
>
<a
class="edit-button"
data-testid="Test-edit"
href="/admin_console/user_management/permissions/team_override_scheme/id"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Yes, Delete"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmButton"
/>
</Memo(LoadingWrapper)>
}
message={
<div>
<p>
<Memo(MemoizedFormattedMessage)
defaultMessage="The permissions in the teams using this scheme will reset to the defaults in the System Scheme. Are you sure you want to delete the {schemeName} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteConfirmQuestion"
values={
Object {
"schemeName": "Test",
}
}
/>
</p>
</div>
}
onCancel={[Function]}
onConfirm={[Function]}
show={false}
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Delete {scheme} scheme?"
id="admin.permissions.permissionsSchemeSummary.deleteSchemeTitle"
values={
Object {
"scheme": "Test",
}
}
/>
}
Edit
</a>
-
<a
class="delete-button"
data-testid="Test-delete"
>
Delete
</a>
</div>
</div>
<div
class="permissions-scheme-summary--description"
>
Test description
</div>
<div
class="permissions-scheme-summary--teams"
/>
</div>
<div
className="permissions-scheme-summary--header"
>
<div
className="title"
>
Test
</div>
<div
className="actions"
>
<Link
className="edit-button"
data-testid="Test-edit"
to="/admin_console/user_management/permissions/team_override_scheme/id"
>
<MemoizedFormattedMessage
defaultMessage="Edit"
id="admin.permissions.permissionsSchemeSummary.edit"
/>
</Link>
-
<a
className="delete-button"
data-testid="Test-delete"
onClick={[Function]}
>
<MemoizedFormattedMessage
defaultMessage="Delete"
id="admin.permissions.permissionsSchemeSummary.delete"
/>
</a>
</div>
</div>
<div
className="permissions-scheme-summary--description"
>
Test description
</div>
<div
className="permissions-scheme-summary--teams"
/>
</div>
`;
@@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import PermissionsSchemeSummary from 'components/admin_console/permission_schemes_settings/permissions_scheme_summary/permissions_scheme_summary';
import ConfirmModal from 'components/confirm_modal';
import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
import PermissionsSchemeSummary from './permissions_scheme_summary';
describe('components/admin_console/permission_schemes_settings/permissions_scheme_summary', () => {
const defaultProps = {
@@ -23,17 +23,33 @@ describe('components/admin_console/permission_schemes_settings/permissions_schem
actions: {
deleteScheme: jest.fn().mockResolvedValue({data: true}),
},
history: {
push: jest.fn(),
},
location: {
pathname: '',
},
match: {
url: '',
},
} as any;
test('should match snapshot on default data', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsSchemeSummary {...defaultProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('Test')).toBeInTheDocument();
expect(screen.getByText('Test description')).toBeInTheDocument();
expect(screen.getByText('Team 1')).toBeInTheDocument();
expect(screen.getByText('Team 2')).toBeInTheDocument();
expect(screen.getByText('Team 3')).toBeInTheDocument();
expect(screen.getByText('Edit')).toBeInTheDocument();
expect(screen.getByText('Delete')).toBeInTheDocument();
});
test('should match snapshot on more than eight teams', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsSchemeSummary
{...defaultProps}
teams={[
@@ -46,26 +62,42 @@ describe('components/admin_console/permission_schemes_settings/permissions_schem
{id: 'ggg', name: 'team-7', display_name: 'Team 7'},
{id: 'hhh', name: 'team-8', display_name: 'Team 8'},
{id: 'iii', name: 'team-9', display_name: 'Team 9'},
{id: 'jjj', name: 'team-9', display_name: 'Team 10'},
{id: 'jjj', name: 'team-10', display_name: 'Team 10'},
]}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
// First 8 teams should be visible
for (let i = 1; i <= 8; i++) {
expect(screen.getByText(`Team ${i}`)).toBeInTheDocument();
}
// Teams 9 and 10 should not be directly visible
expect(screen.queryByText('Team 9')).not.toBeInTheDocument();
expect(screen.queryByText('Team 10')).not.toBeInTheDocument();
// "+2 more" indicator should be shown
expect(screen.getByText('+2 more')).toBeInTheDocument();
});
test('should match snapshot on no teams', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsSchemeSummary
{...defaultProps}
teams={[]}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
expect(screen.getByText('Test')).toBeInTheDocument();
expect(screen.getByText('Test description')).toBeInTheDocument();
expect(screen.queryByText('Team 1')).not.toBeInTheDocument();
});
test('should ask to toggle on row toggle', () => {
test('should ask to toggle on row toggle', async () => {
const deleteScheme = jest.fn().mockResolvedValue({data: true});
const wrapper = shallow(
renderWithContext(
<PermissionsSchemeSummary
{...defaultProps}
actions={{
@@ -73,14 +105,24 @@ describe('components/admin_console/permission_schemes_settings/permissions_schem
}}
/>,
);
expect(deleteScheme).not.toHaveBeenCalled();
wrapper.find('.delete-button').first().simulate('click', {stopPropagation: jest.fn()});
expect(deleteScheme).not.toHaveBeenCalled();
wrapper.find(ConfirmModal).first().props().onCancel?.(true);
expect(deleteScheme).not.toHaveBeenCalled();
wrapper.find('.delete-button').first().simulate('click', {stopPropagation: jest.fn()});
wrapper.find(ConfirmModal).first().props().onConfirm?.(true);
expect(deleteScheme).toHaveBeenCalledWith('id');
// Click delete to open confirm modal
await userEvent.click(screen.getByTestId('Test-delete'));
expect(deleteScheme).not.toHaveBeenCalled();
// Cancel the deletion
await userEvent.click(screen.getByTestId('cancel-button'));
expect(deleteScheme).not.toHaveBeenCalled();
// Click delete again to open confirm modal
await userEvent.click(screen.getByTestId('Test-delete'));
// Confirm the deletion
await userEvent.click(screen.getByRole('button', {name: /yes, delete/i}));
await waitFor(() => {
expect(deleteScheme).toHaveBeenCalledWith('id');
});
});
});
@@ -1,18 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import GeneralConstants from 'mattermost-redux/constants/general';
import PermissionGroup from 'components/admin_console/permission_schemes_settings/permission_group';
import PermissionsTree from 'components/admin_console/permission_schemes_settings/permissions_tree/permissions_tree';
import {renderWithContext} from 'tests/react_testing_utils';
import {LicenseSkus} from 'utils/constants';
import type {Group, Permission} from './types';
jest.mock('components/admin_console/permission_schemes_settings/permission_group', () => {
return jest.fn(() => <div data-testid='permission-group'/>);
});
jest.mock('components/admin_console/permission_schemes_settings/edit_post_time_limit_modal', () => {
return jest.fn(() => null);
});
const PermissionGroup = require('components/admin_console/permission_schemes_settings/permission_group');
describe('components/admin_console/permission_schemes_settings/permission_tree', () => {
const defaultProps = {
scope: 'channel_scope',
@@ -39,73 +48,79 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
customGroupsEnabled: true,
};
beforeEach(() => {
PermissionGroup.mockClear();
});
test('should match snapshot on default data', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree {...defaultProps}/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on read only', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
readOnly={true}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on team scope', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
scope={'team_scope'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on system scope', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
scope={'system_scope'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot on license without LDAPGroups', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
license={{}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should match snapshot with parentRole with permissions', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
parentRole={{permissions: ['invite_user']}}
scope={'system_scope'}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should ask to toggle on row toggle', () => {
const onToggle = jest.fn();
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...defaultProps}
onToggle={onToggle}
/>,
);
wrapper.find(PermissionGroup).first().prop('onChange')(['test_permission', 'test_permission2']);
const calls = PermissionGroup.mock.calls;
const onChange = calls[0][0].onChange;
onChange(['test_permission', 'test_permission2']);
expect(onToggle).toHaveBeenCalledWith('test', ['test_permission', 'test_permission2']);
});
@@ -116,13 +131,13 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
{roleName: GeneralConstants.SYSTEM_USER_ROLE, shouldSeeConvertPrivateToPublic: false},
{roleName: GeneralConstants.SYSTEM_GUEST_ROLE, shouldSeeConvertPrivateToPublic: false},
])('should show convert private channel to public for $roleName: $shouldSeeConvertPrivateToPublic', ({roleName, shouldSeeConvertPrivateToPublic}) => {
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...defaultProps}
role={{name: roleName}}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
if (shouldSeeConvertPrivateToPublic) {
expect(groups[2].permissions).toContain('convert_private_channel_to_public');
} else {
@@ -131,7 +146,7 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
});
test('should hide disabbled integration options', () => {
const wrapper = shallow(
const {container} = renderWithContext(
<PermissionsTree
{...defaultProps}
config={{
@@ -142,17 +157,17 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
}}
/>,
);
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
test('should map groups in the correct order', () => {
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...defaultProps}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
expect(groups[0].id).toStrictEqual('teams');
expect(groups[6].id).toStrictEqual('posts');
expect(groups[7].id).toStrictEqual('integrations');
@@ -171,13 +186,13 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...props}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
expect(groups[3].id).toStrictEqual('playbook_public');
expect(groups[4].id).toStrictEqual('runs');
}));
@@ -193,13 +208,13 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...props}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
expect(groups[3].id).toStrictEqual('playbook_public');
expect(groups[4].id).toStrictEqual('playbook_private');
expect(groups[5].id).toStrictEqual('runs');
@@ -218,12 +233,12 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...props}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
expect(groups[1].permissions).not.toContain('manage_public_channel_auto_translation');
expect(groups[1].permissions).not.toContain('manage_private_channel_auto_translation');
expect(groups[2].permissions).not.toContain('manage_public_channel_auto_translation');
@@ -241,12 +256,12 @@ describe('components/admin_console/permission_schemes_settings/permission_tree',
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTree
{...props}
/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions') as Array<Group | Permission>;
const groups = PermissionGroup.mock.calls[0][0].permissions as Array<Group | Permission>;
expect(groups[1].permissions).toContain('manage_public_channel_auto_translation');
expect(groups[1].permissions).not.toContain('manage_private_channel_auto_translation');
expect(groups[2].permissions).not.toContain('manage_public_channel_auto_translation');
@@ -1,17 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react';
import type {ComponentProps} from 'react';
import PermissionGroup from 'components/admin_console/permission_schemes_settings/permission_group';
import PermissionsTreePlaybooks from 'components/admin_console/permission_schemes_settings/permissions_tree_playbooks';
import {renderWithContext} from 'tests/react_testing_utils';
import {LicenseSkus} from 'utils/constants';
import type {Group, Permission} from './permissions_tree/types';
jest.mock('components/admin_console/permission_schemes_settings/permission_group', () => {
return jest.fn(() => <div data-testid='permission-group'/>);
});
const PermissionGroup = require('components/admin_console/permission_schemes_settings/permission_group');
describe('components/admin_console/permission_schemes_settings/permissions_tree_playbooks', () => {
const defaultProps: ComponentProps<typeof PermissionsTreePlaybooks> = {
role: {name: 'role'},
@@ -24,6 +29,10 @@ describe('components/admin_console/permission_schemes_settings/permissions_tree_
},
};
beforeEach(() => {
PermissionGroup.mockClear();
});
describe('should show playbook permissions', () => {
describe('for non-enterprise license', () => {
['', LicenseSkus.E10, LicenseSkus.Starter, LicenseSkus.Professional].forEach((licenseSku) => test(licenseSku, () => {
@@ -35,11 +44,11 @@ describe('components/admin_console/permission_schemes_settings/permissions_tree_
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTreePlaybooks {...props}/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions');
const groups = PermissionGroup.mock.calls[0][0].permissions;
expect(groups).toHaveLength(2);
expect((groups[0] as Group | Permission).id).toStrictEqual('playbook_public');
expect((groups[1] as Group | Permission).id).toStrictEqual('runs');
@@ -56,11 +65,11 @@ describe('components/admin_console/permission_schemes_settings/permissions_tree_
},
};
const wrapper = shallow(
renderWithContext(
<PermissionsTreePlaybooks {...props}/>,
);
const groups = wrapper.find(PermissionGroup).first().prop('permissions');
const groups = PermissionGroup.mock.calls[0][0].permissions;
expect(groups).toHaveLength(3);
expect((groups[0] as Group | Permission).id).toStrictEqual('playbook_public');
expect((groups[1] as Group | Permission).id).toStrictEqual('playbook_private');