diff --git a/awx/ui_next/src/screens/Credential/Credential.jsx b/awx/ui_next/src/screens/Credential/Credential.jsx
index 705c305e61..31ede881cc 100644
--- a/awx/ui_next/src/screens/Credential/Credential.jsx
+++ b/awx/ui_next/src/screens/Credential/Credential.jsx
@@ -6,12 +6,14 @@ import {
Switch,
useParams,
useLocation,
+ useRouteMatch,
Route,
Redirect,
Link,
} from 'react-router-dom';
import { TabbedCardHeader } from '@components/Card';
import CardCloseButton from '@components/CardCloseButton';
+import { ResourceAccessList } from '@components/ResourceAccessList';
import ContentError from '@components/ContentError';
import RoutedTabs from '@components/RoutedTabs';
import CredentialDetail from './CredentialDetail';
@@ -21,7 +23,10 @@ function Credential({ i18n, setBreadcrumb }) {
const [credential, setCredential] = useState(null);
const [contentError, setContentError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(true);
- const location = useLocation();
+ const { pathname } = useLocation();
+ const match = useRouteMatch({
+ path: '/credentials/:id',
+ });
const { id } = useParams();
useEffect(() => {
@@ -37,18 +42,20 @@ function Credential({ i18n, setBreadcrumb }) {
}
}
fetchData();
- }, [id, setBreadcrumb]);
+ }, [id, pathname, setBreadcrumb]);
const tabsArray = [
{ 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 : (
@@ -56,7 +63,7 @@ function Credential({ i18n, setBreadcrumb }) {
);
- if (location.pathname.endsWith('edit') || location.pathname.endsWith('add')) {
+ if (pathname.endsWith('edit') || pathname.endsWith('add')) {
cardHeader = null;
}
@@ -87,11 +94,40 @@ function Credential({ i18n, setBreadcrumb }) {
to="/credentials/:id/details"
exact
/>
- {credential && (
-
-
-
+ {credential && [
+ }
+ />,
+ credential.organization && (
+ (
+
+ )}
+ />
+ ),
+
+ !hasContentLoading && (
+
+ {match.params.id && (
+
+ {i18n._(`View Credential Details`)}
+
)}
+
+ )
+ }
+ />,
+ ]}
({
+ ...jest.requireActual('react-router-dom'),
+ useRouteMatch: () => ({
+ url: '/credentials/2',
+ params: { id: 2 },
+ }),
+}));
-CredentialsAPI.readDetail.mockResolvedValue({
- data: mockCredentials.results[0],
+CredentialsAPI.readDetail.mockResolvedValueOnce({
+ data: mockCredential,
});
describe('', () => {
let wrapper;
- beforeEach(async () => {
+ test('initially renders user-based credential succesfully', async () => {
await act(async () => {
wrapper = mountWithContexts( {}} />);
});
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 1);
});
- test('initially renders succesfully', async () => {
- expect(wrapper.find('Credential').length).toBe(1);
+ test('initially renders org-based credential succesfully', async () => {
+ CredentialsAPI.readDetail.mockResolvedValueOnce({
+ data: mockOrgCredential,
+ });
+
+ await act(async () => {
+ wrapper = mountWithContexts( {}} />);
+ });
+ 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 () => {
const history = createMemoryHistory({
- initialEntries: ['/credentials/1/foobar'],
+ initialEntries: ['/credentials/2/foobar'],
});
await act(async () => {
wrapper = mountWithContexts( {}} />, {
@@ -38,8 +57,8 @@ describe('', () => {
location: history.location,
match: {
params: { id: 1 },
- url: '/credentials/1/foobar',
- path: '/credentials/1/foobar',
+ url: '/credentials/2/foobar',
+ path: '/credentials/2/foobar',
},
},
},
@@ -47,19 +66,5 @@ describe('', () => {
});
});
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( {}} />);
- });
- await waitForElement(wrapper, 'ContentError', el => el.length === 1);
- expect(wrapper.find('ContentError Title').text()).toEqual(
- 'Something went wrong...'
- );
});
});
diff --git a/awx/ui_next/src/screens/Credential/Credentials.jsx b/awx/ui_next/src/screens/Credential/Credentials.jsx
index 6b9db548ee..ce82540615 100644
--- a/awx/ui_next/src/screens/Credential/Credentials.jsx
+++ b/awx/ui_next/src/screens/Credential/Credentials.jsx
@@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react';
import { Route, Switch } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
-
+import { Config } from '@contexts/Config';
import Breadcrumbs from '@components/Breadcrumbs';
import Credential from './Credential';
import CredentialAdd from './CredentialAdd';
@@ -24,7 +24,9 @@ function Credentials({ i18n }) {
'/credentials': i18n._(t`Credentials`),
'/credentials/add': i18n._(t`Create New Credential`),
[`/credentials/${credential.id}`]: `${credential.name}`,
+ [`/credentials/${credential.id}/edit`]: i18n._(t`Edit Details`),
[`/credentials/${credential.id}/details`]: i18n._(t`Details`),
+ [`/credentials/${credential.id}/access`]: i18n._(t`Access`),
});
},
[i18n]
@@ -35,7 +37,7 @@ function Credentials({ i18n }) {
-
+ {({ me }) => }