mirror of
https://github.com/ansible/awx.git
synced 2026-05-12 11:57:37 -02:30
Show client id/secret in modal after successful application add
This commit is contained in:
@@ -8,7 +8,7 @@ import ApplicationForm from '../shared/ApplicationForm';
|
|||||||
import { ApplicationsAPI } from '../../../api';
|
import { ApplicationsAPI } from '../../../api';
|
||||||
import { CardBody } from '../../../components/Card';
|
import { CardBody } from '../../../components/Card';
|
||||||
|
|
||||||
function ApplicationAdd() {
|
function ApplicationAdd({ onSuccessfulAdd }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [submitError, setSubmitError] = useState(null);
|
const [submitError, setSubmitError] = useState(null);
|
||||||
|
|
||||||
@@ -53,10 +53,9 @@ function ApplicationAdd() {
|
|||||||
const handleSubmit = async ({ ...values }) => {
|
const handleSubmit = async ({ ...values }) => {
|
||||||
values.organization = values.organization.id;
|
values.organization = values.organization.id;
|
||||||
try {
|
try {
|
||||||
const {
|
const { data } = await ApplicationsAPI.create(values);
|
||||||
data: { id },
|
onSuccessfulAdd(data);
|
||||||
} = await ApplicationsAPI.create(values);
|
history.push(`/applications/${data.id}/details`);
|
||||||
history.push(`/applications/${id}/details`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setSubmitError(err);
|
setSubmitError(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,16 @@ const options = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSuccessfulAdd = jest.fn();
|
||||||
|
|
||||||
describe('<ApplicationAdd/>', () => {
|
describe('<ApplicationAdd/>', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
test('should render properly', async () => {
|
test('should render properly', async () => {
|
||||||
ApplicationsAPI.readOptions.mockResolvedValue(options);
|
ApplicationsAPI.readOptions.mockResolvedValue(options);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ApplicationAdd />);
|
wrapper = mountWithContexts(
|
||||||
|
<ApplicationAdd onSuccessfulAdd={onSuccessfulAdd} />
|
||||||
|
);
|
||||||
});
|
});
|
||||||
expect(wrapper.find('ApplicationAdd').length).toBe(1);
|
expect(wrapper.find('ApplicationAdd').length).toBe(1);
|
||||||
expect(wrapper.find('ApplicationForm').length).toBe(1);
|
expect(wrapper.find('ApplicationForm').length).toBe(1);
|
||||||
@@ -59,9 +63,12 @@ describe('<ApplicationAdd/>', () => {
|
|||||||
|
|
||||||
ApplicationsAPI.create.mockResolvedValue({ data: { id: 8 } });
|
ApplicationsAPI.create.mockResolvedValue({ data: { id: 8 } });
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ApplicationAdd />, {
|
wrapper = mountWithContexts(
|
||||||
context: { router: { history } },
|
<ApplicationAdd onSuccessfulAdd={onSuccessfulAdd} />,
|
||||||
});
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
@@ -124,6 +131,7 @@ describe('<ApplicationAdd/>', () => {
|
|||||||
redirect_uris: 'http://www.google.com',
|
redirect_uris: 'http://www.google.com',
|
||||||
});
|
});
|
||||||
expect(history.location.pathname).toBe('/applications/8/details');
|
expect(history.location.pathname).toBe('/applications/8/details');
|
||||||
|
expect(onSuccessfulAdd).toHaveBeenCalledWith({ id: 8 });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should cancel form properly', async () => {
|
test('should cancel form properly', async () => {
|
||||||
@@ -134,9 +142,12 @@ describe('<ApplicationAdd/>', () => {
|
|||||||
|
|
||||||
ApplicationsAPI.create.mockResolvedValue({ data: { id: 8 } });
|
ApplicationsAPI.create.mockResolvedValue({ data: { id: 8 } });
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ApplicationAdd />, {
|
wrapper = mountWithContexts(
|
||||||
context: { router: { history } },
|
<ApplicationAdd onSuccessfulAdd={onSuccessfulAdd} />,
|
||||||
});
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Button[aria-label="Cancel"]').prop('onClick')();
|
wrapper.find('Button[aria-label="Cancel"]').prop('onClick')();
|
||||||
@@ -157,7 +168,9 @@ describe('<ApplicationAdd/>', () => {
|
|||||||
ApplicationsAPI.create.mockRejectedValue(error);
|
ApplicationsAPI.create.mockRejectedValue(error);
|
||||||
ApplicationsAPI.readOptions.mockResolvedValue(options);
|
ApplicationsAPI.readOptions.mockResolvedValue(options);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ApplicationAdd />);
|
wrapper = mountWithContexts(
|
||||||
|
<ApplicationAdd onSuccessfulAdd={onSuccessfulAdd} />
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Formik').prop('onSubmit')({
|
wrapper.find('Formik').prop('onSubmit')({
|
||||||
@@ -181,7 +194,9 @@ describe('<ApplicationAdd/>', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ApplicationAdd />);
|
wrapper = mountWithContexts(
|
||||||
|
<ApplicationAdd onSuccessfulAdd={onSuccessfulAdd} />
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ function ApplicationDetails({
|
|||||||
application.authorization_grant_type
|
application.authorization_grant_type
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Detail label={i18n._(t`Client ID`)} value={application.client_id} />
|
||||||
<Detail
|
<Detail
|
||||||
label={i18n._(t`Redirect uris`)}
|
label={i18n._(t`Redirect uris`)}
|
||||||
value={application.redirect_uris}
|
value={application.redirect_uris}
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ describe('<ApplicationDetails/>', () => {
|
|||||||
expect(wrapper.find('Detail[label="Client type"]').prop('value')).toBe(
|
expect(wrapper.find('Detail[label="Client type"]').prop('value')).toBe(
|
||||||
'Confidential'
|
'Confidential'
|
||||||
);
|
);
|
||||||
|
expect(wrapper.find('Detail[label="Client ID"]').prop('value')).toBe(
|
||||||
|
'b1dmj8xzkbFm1ZQ27ygw2ZeE9I0AXqqeL74fiyk4'
|
||||||
|
);
|
||||||
expect(wrapper.find('Button[aria-label="Edit"]').prop('to')).toBe(
|
expect(wrapper.find('Button[aria-label="Edit"]').prop('to')).toBe(
|
||||||
'/applications/10/edit'
|
'/applications/10/edit'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Card, PageSection } from '@patternfly/react-core';
|
import { Card } from '@patternfly/react-core';
|
||||||
import ApplicationForm from '../shared/ApplicationForm';
|
import ApplicationForm from '../shared/ApplicationForm';
|
||||||
import { ApplicationsAPI } from '../../../api';
|
import { ApplicationsAPI } from '../../../api';
|
||||||
import { CardBody } from '../../../components/Card';
|
import { CardBody } from '../../../components/Card';
|
||||||
@@ -29,22 +29,18 @@ function ApplicationEdit({
|
|||||||
history.push(`/applications/${id}/details`);
|
history.push(`/applications/${id}/details`);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<Card>
|
||||||
<PageSection>
|
<CardBody>
|
||||||
<Card>
|
<ApplicationForm
|
||||||
<CardBody>
|
onSubmit={handleSubmit}
|
||||||
<ApplicationForm
|
application={application}
|
||||||
onSubmit={handleSubmit}
|
onCancel={handleCancel}
|
||||||
application={application}
|
authorizationOptions={authorizationOptions}
|
||||||
onCancel={handleCancel}
|
clientTypeOptions={clientTypeOptions}
|
||||||
authorizationOptions={authorizationOptions}
|
submitError={submitError}
|
||||||
clientTypeOptions={clientTypeOptions}
|
/>
|
||||||
submitError={submitError}
|
</CardBody>
|
||||||
/>
|
</Card>
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</PageSection>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default ApplicationEdit;
|
export default ApplicationEdit;
|
||||||
|
|||||||
@@ -2,13 +2,19 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
import {
|
||||||
|
ClipboardCopy,
|
||||||
|
ClipboardCopyVariant,
|
||||||
|
Modal,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
import ApplicationsList from './ApplicationsList';
|
import ApplicationsList from './ApplicationsList';
|
||||||
import ApplicationAdd from './ApplicationAdd';
|
import ApplicationAdd from './ApplicationAdd';
|
||||||
import Application from './Application';
|
import Application from './Application';
|
||||||
import Breadcrumbs from '../../components/Breadcrumbs';
|
import Breadcrumbs from '../../components/Breadcrumbs';
|
||||||
|
import { Detail, DetailList } from '../../components/DetailList';
|
||||||
|
|
||||||
function Applications({ i18n }) {
|
function Applications({ i18n }) {
|
||||||
|
const [applicationModalSource, setApplicationModalSource] = useState(null);
|
||||||
const [breadcrumbConfig, setBreadcrumbConfig] = useState({
|
const [breadcrumbConfig, setBreadcrumbConfig] = useState({
|
||||||
'/applications': i18n._(t`Applications`),
|
'/applications': i18n._(t`Applications`),
|
||||||
'/applications/add': i18n._(t`Create New Application`),
|
'/applications/add': i18n._(t`Create New Application`),
|
||||||
@@ -36,7 +42,9 @@ function Applications({ i18n }) {
|
|||||||
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/applications/add">
|
<Route path="/applications/add">
|
||||||
<ApplicationAdd />
|
<ApplicationAdd
|
||||||
|
onSuccessfulAdd={app => setApplicationModalSource(app)}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/applications/:id">
|
<Route path="/applications/:id">
|
||||||
<Application setBreadcrumb={buildBreadcrumbConfig} />
|
<Application setBreadcrumb={buildBreadcrumbConfig} />
|
||||||
@@ -45,6 +53,48 @@ function Applications({ i18n }) {
|
|||||||
<ApplicationsList />
|
<ApplicationsList />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
{applicationModalSource && (
|
||||||
|
<Modal
|
||||||
|
aria-label={i18n._(t`Application information`)}
|
||||||
|
isOpen
|
||||||
|
variant="medium"
|
||||||
|
title={i18n._(t`Application information`)}
|
||||||
|
onClose={() => setApplicationModalSource(null)}
|
||||||
|
>
|
||||||
|
<DetailList stacked>
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Name`)}
|
||||||
|
value={applicationModalSource.name}
|
||||||
|
/>
|
||||||
|
{applicationModalSource.client_id && (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Client ID`)}
|
||||||
|
value={
|
||||||
|
<ClipboardCopy
|
||||||
|
isReadOnly
|
||||||
|
variant={ClipboardCopyVariant.expansion}
|
||||||
|
>
|
||||||
|
{applicationModalSource.client_id}
|
||||||
|
</ClipboardCopy>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{applicationModalSource.client_secret && (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Client secret`)}
|
||||||
|
value={
|
||||||
|
<ClipboardCopy
|
||||||
|
isReadOnly
|
||||||
|
variant={ClipboardCopyVariant.expansion}
|
||||||
|
>
|
||||||
|
{applicationModalSource.client_secret}
|
||||||
|
</ClipboardCopy>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DetailList>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,48 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||||
|
|
||||||
import Applications from './Applications';
|
import Applications from './Applications';
|
||||||
|
|
||||||
describe('<Applications />', () => {
|
describe('<Applications />', () => {
|
||||||
let pageWrapper;
|
let wrapper;
|
||||||
let pageSections;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
pageWrapper = mountWithContexts(<Applications />);
|
|
||||||
pageSections = pageWrapper.find('PageSection');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
pageWrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders without crashing', () => {
|
test('renders successfully', () => {
|
||||||
expect(pageWrapper.length).toBe(1);
|
wrapper = mountWithContexts(<Applications />);
|
||||||
|
const pageSections = wrapper.find('PageSection');
|
||||||
|
expect(wrapper.length).toBe(1);
|
||||||
expect(pageSections.length).toBe(1);
|
expect(pageSections.length).toBe(1);
|
||||||
expect(pageSections.first().props().variant).toBe('light');
|
expect(pageSections.first().props().variant).toBe('light');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('shows Application information modal after successful creation', async () => {
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/applications/add'],
|
||||||
|
});
|
||||||
|
wrapper = mountWithContexts(<Applications />, {
|
||||||
|
context: { router: { history } },
|
||||||
|
});
|
||||||
|
expect(wrapper.find('Modal[title="Application information"]').length).toBe(
|
||||||
|
0
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper
|
||||||
|
.find('ApplicationAdd')
|
||||||
|
.props()
|
||||||
|
.onSuccessfulAdd({
|
||||||
|
name: 'test',
|
||||||
|
client_id: 'foobar',
|
||||||
|
client_secret: 'aaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('Modal[title="Application information"]').length).toBe(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user