mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
add cred access tab and update credentials routing
This commit is contained in:
@@ -6,12 +6,14 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
useParams,
|
useParams,
|
||||||
useLocation,
|
useLocation,
|
||||||
|
useRouteMatch,
|
||||||
Route,
|
Route,
|
||||||
Redirect,
|
Redirect,
|
||||||
Link,
|
Link,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import { TabbedCardHeader } from '@components/Card';
|
import { TabbedCardHeader } from '@components/Card';
|
||||||
import CardCloseButton from '@components/CardCloseButton';
|
import CardCloseButton from '@components/CardCloseButton';
|
||||||
|
import { ResourceAccessList } from '@components/ResourceAccessList';
|
||||||
import ContentError from '@components/ContentError';
|
import ContentError from '@components/ContentError';
|
||||||
import RoutedTabs from '@components/RoutedTabs';
|
import RoutedTabs from '@components/RoutedTabs';
|
||||||
import CredentialDetail from './CredentialDetail';
|
import CredentialDetail from './CredentialDetail';
|
||||||
@@ -21,7 +23,10 @@ function Credential({ i18n, setBreadcrumb }) {
|
|||||||
const [credential, setCredential] = useState(null);
|
const [credential, setCredential] = useState(null);
|
||||||
const [contentError, setContentError] = useState(null);
|
const [contentError, setContentError] = useState(null);
|
||||||
const [hasContentLoading, setHasContentLoading] = useState(true);
|
const [hasContentLoading, setHasContentLoading] = useState(true);
|
||||||
const location = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const match = useRouteMatch({
|
||||||
|
path: '/credentials/:id',
|
||||||
|
});
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -37,18 +42,20 @@ function Credential({ i18n, setBreadcrumb }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [id, setBreadcrumb]);
|
}, [id, pathname, setBreadcrumb]);
|
||||||
|
|
||||||
const tabsArray = [
|
const tabsArray = [
|
||||||
{ name: i18n._(t`Details`), link: `/credentials/${id}/details`, id: 0 },
|
{ name: i18n._(t`Details`), link: `/credentials/${id}/details`, id: 0 },
|
||||||
{ name: i18n._(t`Access`), link: `/credentials/${id}/access`, id: 1 },
|
|
||||||
{
|
|
||||||
name: i18n._(t`Notifications`),
|
|
||||||
link: `/credentials/${id}/notifications`,
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (credential && credential.organization) {
|
||||||
|
tabsArray.push({
|
||||||
|
name: i18n._(t`Access`),
|
||||||
|
link: `/credentials/${id}/access`,
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let cardHeader = hasContentLoading ? null : (
|
let cardHeader = hasContentLoading ? null : (
|
||||||
<TabbedCardHeader>
|
<TabbedCardHeader>
|
||||||
<RoutedTabs tabsArray={tabsArray} />
|
<RoutedTabs tabsArray={tabsArray} />
|
||||||
@@ -56,7 +63,7 @@ function Credential({ i18n, setBreadcrumb }) {
|
|||||||
</TabbedCardHeader>
|
</TabbedCardHeader>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (location.pathname.endsWith('edit') || location.pathname.endsWith('add')) {
|
if (pathname.endsWith('edit') || pathname.endsWith('add')) {
|
||||||
cardHeader = null;
|
cardHeader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +94,40 @@ function Credential({ i18n, setBreadcrumb }) {
|
|||||||
to="/credentials/:id/details"
|
to="/credentials/:id/details"
|
||||||
exact
|
exact
|
||||||
/>
|
/>
|
||||||
{credential && (
|
{credential && [
|
||||||
<Route path="/credentials/:id/details">
|
<Route
|
||||||
<CredentialDetail credential={credential} />
|
key="details"
|
||||||
</Route>
|
path="/credentials/:id/details"
|
||||||
|
render={() => <CredentialDetail credential={credential} />}
|
||||||
|
/>,
|
||||||
|
credential.organization && (
|
||||||
|
<Route
|
||||||
|
key="access"
|
||||||
|
path="/credentials/:id/access"
|
||||||
|
render={() => (
|
||||||
|
<ResourceAccessList
|
||||||
|
resource={credential}
|
||||||
|
apiModel={CredentialsAPI}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
<Route
|
||||||
|
key="not-found"
|
||||||
|
path="*"
|
||||||
|
render={() =>
|
||||||
|
!hasContentLoading && (
|
||||||
|
<ContentError isNotFound>
|
||||||
|
{match.params.id && (
|
||||||
|
<Link to={`/credentials/${match.params.id}/details`}>
|
||||||
|
{i18n._(`View Credential Details`)}
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
</ContentError>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
<Route
|
<Route
|
||||||
key="not-found"
|
key="not-found"
|
||||||
path="*"
|
path="*"
|
||||||
|
|||||||
@@ -3,31 +3,50 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { CredentialsAPI } from '@api';
|
import { CredentialsAPI } from '@api';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { mockCredentials } from './shared';
|
import mockCredential from './shared/data.credential.json';
|
||||||
|
import mockOrgCredential from './shared/data.orgCredential.json';
|
||||||
import Credential from './Credential';
|
import Credential from './Credential';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useRouteMatch: () => ({
|
||||||
|
url: '/credentials/2',
|
||||||
|
params: { id: 2 },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
CredentialsAPI.readDetail.mockResolvedValue({
|
CredentialsAPI.readDetail.mockResolvedValueOnce({
|
||||||
data: mockCredentials.results[0],
|
data: mockCredential,
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('<Credential />', () => {
|
describe('<Credential />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
test('initially renders user-based credential succesfully', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
||||||
});
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders succesfully', async () => {
|
test('initially renders org-based credential succesfully', async () => {
|
||||||
expect(wrapper.find('Credential').length).toBe(1);
|
CredentialsAPI.readDetail.mockResolvedValueOnce({
|
||||||
|
data: mockOrgCredential,
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
// org-based credential detail needs access tab
|
||||||
|
await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
||||||
const history = createMemoryHistory({
|
const history = createMemoryHistory({
|
||||||
initialEntries: ['/credentials/1/foobar'],
|
initialEntries: ['/credentials/2/foobar'],
|
||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />, {
|
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />, {
|
||||||
@@ -38,8 +57,8 @@ describe('<Credential />', () => {
|
|||||||
location: history.location,
|
location: history.location,
|
||||||
match: {
|
match: {
|
||||||
params: { id: 1 },
|
params: { id: 1 },
|
||||||
url: '/credentials/1/foobar',
|
url: '/credentials/2/foobar',
|
||||||
path: '/credentials/1/foobar',
|
path: '/credentials/2/foobar',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -47,19 +66,5 @@ describe('<Credential />', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
expect(wrapper.find('ContentError Title').text()).toEqual('Not Found');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show content error if api throws an error', async () => {
|
|
||||||
CredentialsAPI.readDetail.mockImplementationOnce(() =>
|
|
||||||
Promise.reject(new Error())
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
|
||||||
});
|
|
||||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
|
||||||
expect(wrapper.find('ContentError Title').text()).toEqual(
|
|
||||||
'Something went wrong...'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } 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 { Config } from '@contexts/Config';
|
||||||
import Breadcrumbs from '@components/Breadcrumbs';
|
import Breadcrumbs from '@components/Breadcrumbs';
|
||||||
import Credential from './Credential';
|
import Credential from './Credential';
|
||||||
import CredentialAdd from './CredentialAdd';
|
import CredentialAdd from './CredentialAdd';
|
||||||
@@ -24,7 +24,9 @@ function Credentials({ i18n }) {
|
|||||||
'/credentials': i18n._(t`Credentials`),
|
'/credentials': i18n._(t`Credentials`),
|
||||||
'/credentials/add': i18n._(t`Create New Credential`),
|
'/credentials/add': i18n._(t`Create New Credential`),
|
||||||
[`/credentials/${credential.id}`]: `${credential.name}`,
|
[`/credentials/${credential.id}`]: `${credential.name}`,
|
||||||
|
[`/credentials/${credential.id}/edit`]: i18n._(t`Edit Details`),
|
||||||
[`/credentials/${credential.id}/details`]: i18n._(t`Details`),
|
[`/credentials/${credential.id}/details`]: i18n._(t`Details`),
|
||||||
|
[`/credentials/${credential.id}/access`]: i18n._(t`Access`),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[i18n]
|
[i18n]
|
||||||
@@ -35,7 +37,7 @@ function Credentials({ i18n }) {
|
|||||||
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
<Breadcrumbs breadcrumbConfig={breadcrumbConfig} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/credentials/add">
|
<Route path="/credentials/add">
|
||||||
<CredentialAdd />
|
<Config>{({ me }) => <CredentialAdd me={me || {}} />}</Config>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/credentials/:id">
|
<Route path="/credentials/:id">
|
||||||
<Credential setBreadcrumb={buildBreadcrumbConfig} />
|
<Credential setBreadcrumb={buildBreadcrumbConfig} />
|
||||||
|
|||||||
Reference in New Issue
Block a user