mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Teams Access List using Resource Access component
This commit is contained in:
@@ -29,7 +29,7 @@ class Teams extends Base {
|
|||||||
return this.http.options(`${this.baseUrl}${teamId}/roles/`);
|
return this.http.options(`${this.baseUrl}${teamId}/roles/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
readUsersAccess(teamId, params) {
|
readAccessList(teamId, params) {
|
||||||
return this.http.get(`${this.baseUrl}${teamId}/access_list/`, {
|
return this.http.get(`${this.baseUrl}${teamId}/access_list/`, {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
@@ -38,7 +38,6 @@ class Teams extends Base {
|
|||||||
readUsersAccessOptions(teamId) {
|
readUsersAccessOptions(teamId) {
|
||||||
return this.http.options(`${this.baseUrl}${teamId}/users/`);
|
return this.http.options(`${this.baseUrl}${teamId}/users/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Teams;
|
export default Teams;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import TeamDetail from './TeamDetail';
|
|||||||
import TeamEdit from './TeamEdit';
|
import TeamEdit from './TeamEdit';
|
||||||
import { TeamsAPI } from '../../api';
|
import { TeamsAPI } from '../../api';
|
||||||
import TeamAccessList from './TeamAccess';
|
import TeamAccessList from './TeamAccess';
|
||||||
import TeamUsersList from './TeamUsers';
|
import { ResourceAccessList } from '../../components/ResourceAccessList';
|
||||||
|
|
||||||
function Team({ i18n, setBreadcrumb }) {
|
function Team({ i18n, setBreadcrumb }) {
|
||||||
const [team, setTeam] = useState(null);
|
const [team, setTeam] = useState(null);
|
||||||
@@ -97,7 +97,7 @@ function Team({ i18n, setBreadcrumb }) {
|
|||||||
)}
|
)}
|
||||||
{team && (
|
{team && (
|
||||||
<Route path="/teams/:id/access">
|
<Route path="/teams/:id/access">
|
||||||
<TeamUsersList />
|
<ResourceAccessList resource={team} apiModel={TeamsAPI} />
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
{team && (
|
{team && (
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import 'styled-components/macro';
|
|
||||||
import React from 'react';
|
|
||||||
import { string, func } from 'prop-types';
|
|
||||||
import { withI18n } from '@lingui/react';
|
|
||||||
import { t } from '@lingui/macro';
|
|
||||||
import {
|
|
||||||
DataListItem,
|
|
||||||
DataListItemCells,
|
|
||||||
DataListItemRow,
|
|
||||||
Label as PFLabel,
|
|
||||||
} from '@patternfly/react-core';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import DataListCell from '../../../components/DataListCell';
|
|
||||||
|
|
||||||
import { User } from '../../../types';
|
|
||||||
|
|
||||||
function TeamUserListItem({ user, disassociateRole, detailUrl, i18n }) {
|
|
||||||
const labelId = `check-action-${user.id}`;
|
|
||||||
const Label = styled.b`
|
|
||||||
margin-right: 20px;
|
|
||||||
`;
|
|
||||||
const hasDirectRoles = user.summary_fields.direct_access.length > 0;
|
|
||||||
const hasIndirectRoles = user.summary_fields.indirect_access.length > 0;
|
|
||||||
return (
|
|
||||||
<DataListItem key={user.id} aria-labelledby={labelId} id={`${user.id}`}>
|
|
||||||
<DataListItemRow>
|
|
||||||
<DataListItemCells
|
|
||||||
dataListCells={[
|
|
||||||
<DataListCell aria-label={i18n._(t`username`)} key="username">
|
|
||||||
<Link id={labelId} to={`${detailUrl}`}>
|
|
||||||
<b>{user.username}</b>
|
|
||||||
</Link>
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell aria-label={i18n._(t`first name`)} key="first name">
|
|
||||||
{user.first_name && (
|
|
||||||
<>
|
|
||||||
<Label>{i18n._(t`First`)}</Label>
|
|
||||||
<span>{user.first_name}</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell aria-label={i18n._(t`last name`)} key="last name">
|
|
||||||
{user.last_name && (
|
|
||||||
<>
|
|
||||||
<Label>{i18n._(t`Last`)}</Label>
|
|
||||||
<span>{user.last}</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell aria-label={i18n._(t`roles`)} key="role">
|
|
||||||
{hasDirectRoles && (
|
|
||||||
<>
|
|
||||||
<Label>{i18n._(t`Roles`)}</Label>
|
|
||||||
<span>
|
|
||||||
{user.summary_fields.direct_access.map(role =>
|
|
||||||
role.role.name !== 'Read' ? (
|
|
||||||
<PFLabel
|
|
||||||
aria-label={role.role.name}
|
|
||||||
key={role.role.id}
|
|
||||||
role={role.role}
|
|
||||||
onClose={() => disassociateRole(role.role)}
|
|
||||||
>
|
|
||||||
{role.role.name}
|
|
||||||
</PFLabel>
|
|
||||||
) : null
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DataListCell>,
|
|
||||||
<DataListCell
|
|
||||||
aria-label={i18n._(t`indirect role`)}
|
|
||||||
key="indirectRole"
|
|
||||||
>
|
|
||||||
{hasIndirectRoles && (
|
|
||||||
<>
|
|
||||||
<Label>{i18n._(t`Indirect Roles`)}</Label>
|
|
||||||
<span>
|
|
||||||
{user.summary_fields.indirect_access.map(role => (
|
|
||||||
<PFLabel key={role.role.id} credential={role.role}>
|
|
||||||
{role.role.name}
|
|
||||||
</PFLabel>
|
|
||||||
))}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DataListCell>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</DataListItemRow>
|
|
||||||
</DataListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TeamUserListItem.propTypes = {
|
|
||||||
user: User.isRequired,
|
|
||||||
detailUrl: string.isRequired,
|
|
||||||
disassociateRole: func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withI18n()(TeamUserListItem);
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
|
||||||
|
|
||||||
import TeamUserListItem from './TeamUserListItem';
|
|
||||||
|
|
||||||
describe('<TeamUserListItem />', () => {
|
|
||||||
const user = {
|
|
||||||
id: 1,
|
|
||||||
name: 'Team 1',
|
|
||||||
summary_fields: {
|
|
||||||
direct_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 40,
|
|
||||||
name: 'Member',
|
|
||||||
description: 'User is a member of the team',
|
|
||||||
resource_name: ' Team 1 Org 0',
|
|
||||||
resource_type: 'team',
|
|
||||||
related: {
|
|
||||||
team: '/api/v2/teams/1/',
|
|
||||||
},
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indirect_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 2,
|
|
||||||
name: 'Admin',
|
|
||||||
description: 'Can manage all aspects of the organization',
|
|
||||||
resource_name: ' Organization 0',
|
|
||||||
resource_type: 'organization',
|
|
||||||
related: {
|
|
||||||
organization: '/api/v2/organizations/1/',
|
|
||||||
},
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['admin_role', 'member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
user_capabilities: {
|
|
||||||
edit: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
username: 'Casey',
|
|
||||||
firstname: 'The',
|
|
||||||
lastname: 'Cat',
|
|
||||||
email: '',
|
|
||||||
};
|
|
||||||
test('initially renders succesfully', () => {
|
|
||||||
mountWithContexts(
|
|
||||||
<TeamUserListItem
|
|
||||||
user={user}
|
|
||||||
detailUrl="/users/1/details"
|
|
||||||
disassociateRole={() => {}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
test('initially render prop items', () => {
|
|
||||||
const wrapper = mountWithContexts(
|
|
||||||
<TeamUserListItem
|
|
||||||
user={user}
|
|
||||||
detailUrl="/users/1/details"
|
|
||||||
disassociateRole={() => {}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(wrapper.find('DataListCell[aria-label="username"]').length).toBe(1);
|
|
||||||
expect(wrapper.find('DataListCell[aria-label="first name"]').length).toBe(
|
|
||||||
1
|
|
||||||
);
|
|
||||||
expect(wrapper.find('DataListCell[aria-label="last name"]').length).toBe(1);
|
|
||||||
expect(wrapper.find('DataListCell[aria-label="roles"]').length).toBe(1);
|
|
||||||
expect(
|
|
||||||
wrapper.find('DataListCell[aria-label="indirect role"]').length
|
|
||||||
).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
import React, { useEffect, useCallback, useState } from 'react';
|
|
||||||
import { useLocation, useRouteMatch, useParams } from 'react-router-dom';
|
|
||||||
import { withI18n } from '@lingui/react';
|
|
||||||
import { t } from '@lingui/macro';
|
|
||||||
|
|
||||||
import { Button } from '@patternfly/react-core';
|
|
||||||
import { TeamsAPI, UsersAPI } from '../../../api';
|
|
||||||
import useRequest, { useDeleteItems } from '../../../util/useRequest';
|
|
||||||
import AlertModal from '../../../components/AlertModal';
|
|
||||||
import DataListToolbar from '../../../components/DataListToolbar';
|
|
||||||
import ErrorDetail from '../../../components/ErrorDetail';
|
|
||||||
import PaginatedDataList, {
|
|
||||||
ToolbarAddButton,
|
|
||||||
} from '../../../components/PaginatedDataList';
|
|
||||||
import { getQSConfig, parseQueryString } from '../../../util/qs';
|
|
||||||
|
|
||||||
import TeamUserListItem from './TeamUserListItem';
|
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('user', {
|
|
||||||
page: 1,
|
|
||||||
page_size: 20,
|
|
||||||
order_by: 'username',
|
|
||||||
});
|
|
||||||
|
|
||||||
function TeamUsersList({ i18n }) {
|
|
||||||
const location = useLocation();
|
|
||||||
const match = useRouteMatch();
|
|
||||||
const { id: teamId } = useParams();
|
|
||||||
const [roleToDisassociate, setRoleToDisassociate] = useState([]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
result: { users, itemCount, actions },
|
|
||||||
error: contentError,
|
|
||||||
isLoading,
|
|
||||||
request: fetchRoles,
|
|
||||||
} = useRequest(
|
|
||||||
useCallback(async () => {
|
|
||||||
const params = parseQueryString(QS_CONFIG, location.search);
|
|
||||||
const [response, actionsResponse] = await Promise.all([
|
|
||||||
TeamsAPI.readUsersAccess(teamId, params),
|
|
||||||
TeamsAPI.readUsersAccessOptions(teamId),
|
|
||||||
]);
|
|
||||||
return {
|
|
||||||
users: response.data.results,
|
|
||||||
itemCount: response.data.count,
|
|
||||||
actions: actionsResponse.data.actions,
|
|
||||||
};
|
|
||||||
}, [location, teamId]),
|
|
||||||
{
|
|
||||||
users: [],
|
|
||||||
itemCount: 0,
|
|
||||||
actions: {},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchRoles();
|
|
||||||
}, [fetchRoles]);
|
|
||||||
|
|
||||||
const {
|
|
||||||
isLoading: isDeleteLoading,
|
|
||||||
deleteItems: disassociateRole,
|
|
||||||
deletionError,
|
|
||||||
clearDeletionError,
|
|
||||||
} = useDeleteItems(
|
|
||||||
useCallback(async () => {
|
|
||||||
UsersAPI.disassociateRole(
|
|
||||||
roleToDisassociate[0].id,
|
|
||||||
roleToDisassociate[1].id
|
|
||||||
);
|
|
||||||
}, [roleToDisassociate]),
|
|
||||||
{
|
|
||||||
qsConfig: QS_CONFIG,
|
|
||||||
fetchItems: fetchRoles,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleRoleDisassociation = async () => {
|
|
||||||
await disassociateRole();
|
|
||||||
setRoleToDisassociate(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasContentLoading = isDeleteLoading || isLoading;
|
|
||||||
const canAdd = actions && actions.POST;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PaginatedDataList
|
|
||||||
contentError={contentError}
|
|
||||||
hasContentLoading={hasContentLoading}
|
|
||||||
items={users}
|
|
||||||
itemCount={itemCount}
|
|
||||||
pluralizedItemName={i18n._(t`Users`)}
|
|
||||||
qsConfig={QS_CONFIG}
|
|
||||||
toolbarSearchColumns={[
|
|
||||||
{
|
|
||||||
name: i18n._(t`User Name`),
|
|
||||||
key: 'username',
|
|
||||||
isDefault: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`First Name`),
|
|
||||||
key: 'first_name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Last Name`),
|
|
||||||
key: 'last_name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Email`),
|
|
||||||
key: 'email',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
toolbarSortColumns={[
|
|
||||||
{
|
|
||||||
name: i18n._(t`User Name`),
|
|
||||||
key: 'username',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`First Name`),
|
|
||||||
key: 'first_name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Last Name`),
|
|
||||||
key: 'last_name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18n._(t`Email`),
|
|
||||||
key: 'email',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
renderToolbar={props => (
|
|
||||||
<DataListToolbar
|
|
||||||
{...props}
|
|
||||||
qsConfig={QS_CONFIG}
|
|
||||||
additionalControls={[
|
|
||||||
...(canAdd
|
|
||||||
? [<ToolbarAddButton key="add" linkTo="/users/add" />]
|
|
||||||
: []),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
renderItem={user => (
|
|
||||||
<TeamUserListItem
|
|
||||||
key={user.id}
|
|
||||||
user={user}
|
|
||||||
detailUrl={`/users/${user.id}/details`}
|
|
||||||
disassociateRole={role => setRoleToDisassociate([user, role])}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
emptyStateControls={
|
|
||||||
canAdd ? (
|
|
||||||
<ToolbarAddButton key="add" linkTo={`${match.url}/add`} />
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{roleToDisassociate?.length > 0 && (
|
|
||||||
<AlertModal
|
|
||||||
variant="danger"
|
|
||||||
title={i18n._(t`Disassociate roles`)}
|
|
||||||
isOpen={roleToDisassociate}
|
|
||||||
onClose={() => setRoleToDisassociate(null)}
|
|
||||||
actions={[
|
|
||||||
<Button
|
|
||||||
key="disassociate"
|
|
||||||
variant="danger"
|
|
||||||
aria-label={i18n._(t`confirm disassociation`)}
|
|
||||||
onClick={() => handleRoleDisassociation()}
|
|
||||||
>
|
|
||||||
{i18n._(t`Disassociate`)}
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key="cancel"
|
|
||||||
variant="secondary"
|
|
||||||
aria-label={i18n._(t`cancel disassociation`)}
|
|
||||||
onClick={() => setRoleToDisassociate(null)}
|
|
||||||
>
|
|
||||||
{i18n._(t`Cancel`)}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<div>{i18n._(t`This action will disassociate the following:`)}</div>
|
|
||||||
<span>{roleToDisassociate.name}</span>
|
|
||||||
</AlertModal>
|
|
||||||
)}
|
|
||||||
<AlertModal
|
|
||||||
isOpen={deletionError}
|
|
||||||
variant="error"
|
|
||||||
title={i18n._(t`Error!`)}
|
|
||||||
onClose={clearDeletionError}
|
|
||||||
>
|
|
||||||
{i18n._(t`Failed to disassociate one or more roles.`)}
|
|
||||||
<ErrorDetail error={deletionError} />
|
|
||||||
</AlertModal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withI18n()(TeamUsersList);
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { act } from 'react-dom/test-utils';
|
|
||||||
import { TeamsAPI, UsersAPI } from '../../../api';
|
|
||||||
import {
|
|
||||||
mountWithContexts,
|
|
||||||
waitForElement,
|
|
||||||
} from '../../../../testUtils/enzymeHelpers';
|
|
||||||
|
|
||||||
import TeamUsersList from './TeamUsersList';
|
|
||||||
|
|
||||||
jest.mock('../../../api/models/Teams');
|
|
||||||
jest.mock('../../../api/models/Users');
|
|
||||||
|
|
||||||
const teamUsersList = {
|
|
||||||
data: {
|
|
||||||
count: 3,
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'user',
|
|
||||||
url: '',
|
|
||||||
summary_fields: {
|
|
||||||
direct_access: [],
|
|
||||||
indirect_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
created: '2020-06-19T12:55:13.138692Z',
|
|
||||||
username: 'admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
email: 'a@g.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
type: 'user',
|
|
||||||
url: '',
|
|
||||||
summary_fields: {
|
|
||||||
direct_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 40,
|
|
||||||
name: 'Member',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 41,
|
|
||||||
name: 'Read',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indirect_access: [],
|
|
||||||
},
|
|
||||||
created: '2020-06-19T13:01:44.183577Z',
|
|
||||||
username: 'jt_admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
email: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
type: 'user',
|
|
||||||
url: '',
|
|
||||||
summary_fields: {
|
|
||||||
direct_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 40,
|
|
||||||
name: 'Alex',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 41,
|
|
||||||
name: 'Read',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indirect_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 2,
|
|
||||||
name: 'Admin',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['admin_role', 'member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
created: '2020-06-19T13:01:43.674349Z',
|
|
||||||
username: 'org_admin',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
email: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
type: 'user',
|
|
||||||
url: '',
|
|
||||||
summary_fields: {
|
|
||||||
direct_access: [
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 40,
|
|
||||||
name: 'Savannah',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: {
|
|
||||||
id: 41,
|
|
||||||
name: 'Read',
|
|
||||||
user_capabilities: {
|
|
||||||
unattach: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
descendant_roles: ['member_role', 'read_role'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indirect_access: [],
|
|
||||||
},
|
|
||||||
created: '2020-06-19T13:01:43.868499Z',
|
|
||||||
username: 'org_member',
|
|
||||||
first_name: '',
|
|
||||||
last_name: '',
|
|
||||||
email: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('<TeamUsersList />', () => {
|
|
||||||
let wrapper;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TeamsAPI.readUsersAccess = jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: teamUsersList.data,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
TeamsAPI.readUsersAccessOptions = jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: {
|
|
||||||
actions: {
|
|
||||||
GET: {},
|
|
||||||
POST: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
afterEach(() => {
|
|
||||||
wrapper.unmount();
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should load and render users', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
wrapper = mountWithContexts(<TeamUsersList />);
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
expect(wrapper.find('TeamUserListItem')).toHaveLength(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should disassociate role', async () => {
|
|
||||||
await act(async () => {
|
|
||||||
wrapper = mountWithContexts(<TeamUsersList />);
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Label[aria-label="Member"]').prop('onClose')({
|
|
||||||
id: 1,
|
|
||||||
name: 'Member',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
expect(wrapper.find('AlertModal[title="Disassociate roles"]').length).toBe(
|
|
||||||
1
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper
|
|
||||||
.find('Button[aria-label="confirm disassociation"]')
|
|
||||||
.prop('onClick')();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(UsersAPI.disassociateRole).toHaveBeenCalledTimes(1);
|
|
||||||
expect(TeamsAPI.readUsersAccess).toHaveBeenCalledTimes(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show disassociation error', async () => {
|
|
||||||
UsersAPI.disassociateRole.mockResolvedValue(
|
|
||||||
new Error({
|
|
||||||
response: {
|
|
||||||
config: {
|
|
||||||
method: 'post',
|
|
||||||
url: '/api/v2/users/1',
|
|
||||||
},
|
|
||||||
data: 'An error occurred',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
wrapper = mountWithContexts(<TeamUsersList />);
|
|
||||||
});
|
|
||||||
waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Label[aria-label="Member"]').prop('onClose')({
|
|
||||||
id: 1,
|
|
||||||
name: 'Member',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapper.update();
|
|
||||||
expect(wrapper.find('AlertModal[title="Disassociate roles"]').length).toBe(
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
wrapper
|
|
||||||
.find('Button[aria-label="confirm disassociation"]')
|
|
||||||
.prop('onClick')();
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapper.update();
|
|
||||||
expect(UsersAPI.disassociateRole).toHaveBeenCalled();
|
|
||||||
|
|
||||||
const modal = wrapper.find('Modal');
|
|
||||||
expect(modal).toHaveLength(1);
|
|
||||||
expect(modal.prop('title')).toEqual('Error!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export {
|
|
||||||
default
|
|
||||||
}
|
|
||||||
from './TeamUsersList'
|
|
||||||
Reference in New Issue
Block a user