mirror of
https://github.com/ansible/awx.git
synced 2026-05-10 02:47:36 -02:30
Merge pull request #8369 from nixocio/ui_issue_5685
Add general updates for User screen
Reviewed-by: Kersom
https://github.com/nixocio
This commit is contained in:
@@ -3,7 +3,7 @@ import { Link, useHistory } from 'react-router-dom';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { Button } from '@patternfly/react-core';
|
import { Button, Label } from '@patternfly/react-core';
|
||||||
import AlertModal from '../../../components/AlertModal';
|
import AlertModal from '../../../components/AlertModal';
|
||||||
import { CardBody, CardActionsRow } from '../../../components/Card';
|
import { CardBody, CardActionsRow } from '../../../components/Card';
|
||||||
import DeleteButton from '../../../components/DeleteButton';
|
import DeleteButton from '../../../components/DeleteButton';
|
||||||
@@ -46,6 +46,13 @@ function UserDetail({ user, i18n }) {
|
|||||||
user_type = i18n._(t`Normal User`);
|
user_type = i18n._(t`Normal User`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let userAuthType;
|
||||||
|
if (user.ldap_dn) {
|
||||||
|
userAuthType = i18n._(t`LDAP`);
|
||||||
|
} else if (user.auth.length > 0) {
|
||||||
|
userAuthType = i18n._(t`SOCIAL`);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<DetailList>
|
<DetailList>
|
||||||
@@ -58,6 +65,14 @@ function UserDetail({ user, i18n }) {
|
|||||||
<Detail label={i18n._(t`First Name`)} value={`${first_name}`} />
|
<Detail label={i18n._(t`First Name`)} value={`${first_name}`} />
|
||||||
<Detail label={i18n._(t`Last Name`)} value={`${last_name}`} />
|
<Detail label={i18n._(t`Last Name`)} value={`${last_name}`} />
|
||||||
<Detail label={i18n._(t`User Type`)} value={`${user_type}`} />
|
<Detail label={i18n._(t`User Type`)} value={`${user_type}`} />
|
||||||
|
{userAuthType && (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Type`)}
|
||||||
|
value={
|
||||||
|
<Label aria-label={i18n._(t`login type`)}>{userAuthType}</Label>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{last_login && (
|
{last_login && (
|
||||||
<Detail
|
<Detail
|
||||||
label={i18n._(t`Last Login`)}
|
label={i18n._(t`Last Login`)}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import mockDetails from '../data.user.json';
|
|||||||
jest.mock('../../../api');
|
jest.mock('../../../api');
|
||||||
|
|
||||||
describe('<UserDetail />', () => {
|
describe('<UserDetail />', () => {
|
||||||
test('initially renders succesfully', () => {
|
test('initially renders successfully', () => {
|
||||||
mountWithContexts(<UserDetail user={mockDetails} />);
|
mountWithContexts(<UserDetail user={mockDetails} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ describe('<UserDetail />', () => {
|
|||||||
expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label);
|
expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label);
|
||||||
expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value);
|
expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertDetail('Username', mockDetails.username);
|
assertDetail('Username', mockDetails.username);
|
||||||
assertDetail('Email', mockDetails.email);
|
assertDetail('Email', mockDetails.email);
|
||||||
assertDetail('First Name', mockDetails.first_name);
|
assertDetail('First Name', mockDetails.first_name);
|
||||||
@@ -29,6 +30,7 @@ describe('<UserDetail />', () => {
|
|||||||
assertDetail('User Type', 'System Administrator');
|
assertDetail('User Type', 'System Administrator');
|
||||||
assertDetail('Last Login', `11/4/2019, 11:12:36 PM`);
|
assertDetail('Last Login', `11/4/2019, 11:12:36 PM`);
|
||||||
assertDetail('Created', `10/28/2019, 3:01:07 PM`);
|
assertDetail('Created', `10/28/2019, 3:01:07 PM`);
|
||||||
|
assertDetail('Type', `SOCIAL`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('User Type Detail should render expected strings', async () => {
|
test('User Type Detail should render expected strings', async () => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
DataListItem,
|
DataListItem,
|
||||||
DataListItemCells,
|
DataListItemCells,
|
||||||
DataListItemRow,
|
DataListItemRow,
|
||||||
|
Label,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
@@ -31,6 +32,9 @@ function UserListItem({ user, isSelected, onSelect, detailUrl, i18n }) {
|
|||||||
user_type = i18n._(t`Normal User`);
|
user_type = i18n._(t`Normal User`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ldapUser = user.ldap_dn;
|
||||||
|
const socialAuthUser = user.auth.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataListItem key={user.id} aria-labelledby={labelId} id={`${user.id}`}>
|
<DataListItem key={user.id} aria-labelledby={labelId} id={`${user.id}`}>
|
||||||
<DataListItemRow>
|
<DataListItemRow>
|
||||||
@@ -43,9 +47,25 @@ function UserListItem({ user, isSelected, onSelect, detailUrl, i18n }) {
|
|||||||
<DataListItemCells
|
<DataListItemCells
|
||||||
dataListCells={[
|
dataListCells={[
|
||||||
<DataListCell key="username" aria-label={i18n._(t`username`)}>
|
<DataListCell key="username" aria-label={i18n._(t`username`)}>
|
||||||
<Link to={`${detailUrl}`} id={labelId}>
|
<span id={labelId}>
|
||||||
<b>{user.username}</b>
|
<Link to={`${detailUrl}`} id={labelId}>
|
||||||
</Link>
|
<b>{user.username}</b>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
{ldapUser && (
|
||||||
|
<span css="margin-left: 12px">
|
||||||
|
<Label aria-label={i18n._(t`ldap user`)}>
|
||||||
|
{i18n._(t`LDAP`)}
|
||||||
|
</Label>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{socialAuthUser && (
|
||||||
|
<span css="margin-left: 12px">
|
||||||
|
<Label aria-label={i18n._(t`social login`)}>
|
||||||
|
{i18n._(t`SOCIAL`)}
|
||||||
|
</Label>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell
|
<DataListCell
|
||||||
key="first-name"
|
key="first-name"
|
||||||
|
|||||||
@@ -35,10 +35,13 @@ describe('UserListItem with full permissions', () => {
|
|||||||
expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy();
|
expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display user type', () => {
|
test('should display user data', () => {
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('DataListCell[aria-label="user type"]').prop('children')
|
wrapper.find('DataListCell[aria-label="user type"]').prop('children')
|
||||||
).toEqual('System Administrator');
|
).toEqual('System Administrator');
|
||||||
|
expect(
|
||||||
|
wrapper.find('Label[aria-label="social login"]').prop('children')
|
||||||
|
).toEqual('SOCIAL');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
"is_system_auditor": false,
|
"is_system_auditor": false,
|
||||||
"ldap_dn": "",
|
"ldap_dn": "",
|
||||||
"last_login": "2019-11-04T23:12:36.777783Z",
|
"last_login": "2019-11-04T23:12:36.777783Z",
|
||||||
"external_account": null,
|
"external_account": "social",
|
||||||
"auth": []
|
"auth": [
|
||||||
|
{
|
||||||
|
"provider": "github-org",
|
||||||
|
"uid": "9053044"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,10 @@ function UserFormFields({ user, i18n }) {
|
|||||||
const [organization, setOrganization] = useState(null);
|
const [organization, setOrganization] = useState(null);
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
|
const ldapUser = user.ldap_dn;
|
||||||
|
const socialAuthUser = user.auth?.length > 0;
|
||||||
|
const externalAccount = user.external_account;
|
||||||
|
|
||||||
const userTypeOptions = [
|
const userTypeOptions = [
|
||||||
{
|
{
|
||||||
value: 'normal',
|
value: 'normal',
|
||||||
@@ -63,8 +67,12 @@ function UserFormFields({ user, i18n }) {
|
|||||||
label={i18n._(t`Username`)}
|
label={i18n._(t`Username`)}
|
||||||
name="username"
|
name="username"
|
||||||
type="text"
|
type="text"
|
||||||
validate={required(null, i18n)}
|
validate={
|
||||||
isRequired
|
!ldapUser && externalAccount === null
|
||||||
|
? required(null, i18n)
|
||||||
|
: () => undefined
|
||||||
|
}
|
||||||
|
isRequired={!ldapUser && externalAccount === null}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
id="user-email"
|
id="user-email"
|
||||||
@@ -73,28 +81,32 @@ function UserFormFields({ user, i18n }) {
|
|||||||
validate={requiredEmail(i18n)}
|
validate={requiredEmail(i18n)}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<PasswordField
|
{!ldapUser && !(socialAuthUser && externalAccount) && (
|
||||||
id="user-password"
|
<>
|
||||||
label={i18n._(t`Password`)}
|
<PasswordField
|
||||||
name="password"
|
id="user-password"
|
||||||
validate={
|
label={i18n._(t`Password`)}
|
||||||
!user.id
|
name="password"
|
||||||
? required(i18n._(t`This field must not be blank`), i18n)
|
validate={
|
||||||
: () => undefined
|
!user.id
|
||||||
}
|
? required(i18n._(t`This field must not be blank`), i18n)
|
||||||
isRequired={!user.id}
|
: () => undefined
|
||||||
/>
|
}
|
||||||
<PasswordField
|
isRequired={!user.id}
|
||||||
id="user-confirm-password"
|
/>
|
||||||
label={i18n._(t`Confirm Password`)}
|
<PasswordField
|
||||||
name="confirm_password"
|
id="user-confirm-password"
|
||||||
validate={
|
label={i18n._(t`Confirm Password`)}
|
||||||
!user.id
|
name="confirm_password"
|
||||||
? required(i18n._(t`This field must not be blank`), i18n)
|
validate={
|
||||||
: () => undefined
|
!user.id
|
||||||
}
|
? required(i18n._(t`This field must not be blank`), i18n)
|
||||||
isRequired={!user.id}
|
: () => undefined
|
||||||
/>
|
}
|
||||||
|
isRequired={!user.id}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<FormField
|
<FormField
|
||||||
id="user-first-name"
|
id="user-first-name"
|
||||||
label={i18n._(t`First Name`)}
|
label={i18n._(t`First Name`)}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ describe('<UserForm />', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<UserForm
|
<UserForm
|
||||||
user={mockData}
|
user={{ ...mockData, external_account: '', auth: [] }}
|
||||||
handleSubmit={jest.fn()}
|
handleSubmit={jest.fn()}
|
||||||
handleCancel={jest.fn()}
|
handleCancel={jest.fn()}
|
||||||
/>
|
/>
|
||||||
@@ -125,6 +125,22 @@ describe('<UserForm />', () => {
|
|||||||
expect(passwordFields.at(1).prop('isRequired')).toBe(false);
|
expect(passwordFields.at(1).prop('isRequired')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('password fields are not displayed for social/ldap login', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<UserForm
|
||||||
|
user={mockData}
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordFields = wrapper.find('PasswordField');
|
||||||
|
|
||||||
|
expect(passwordFields.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('should call handleSubmit when Submit button is clicked', async () => {
|
test('should call handleSubmit when Submit button is clicked', async () => {
|
||||||
const handleSubmit = jest.fn();
|
const handleSubmit = jest.fn();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user