diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx
index 39c1b4dfbf..f8ce4879c8 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryGroupDetail/InventoryGroupDetail.jsx
@@ -17,7 +17,7 @@ import InventoryGroupsDeleteModal from '../shared/InventoryGroupsDeleteModal';
function InventoryGroupDetail({ i18n, inventoryGroup }) {
const {
- summary_fields: { created_by, modified_by },
+ summary_fields: { created_by, modified_by, user_capabilities },
created,
modified,
name,
@@ -54,24 +54,28 @@ function InventoryGroupDetail({ i18n, inventoryGroup }) {
/>
-
-
- history.push(`/inventories/inventory/${params.id}/groups`)
- }
- />
+ {user_capabilities?.edit && (
+
+ )}
+ {user_capabilities?.delete && (
+
+ history.push(`/inventories/inventory/${params.id}/groups`)
+ }
+ />
+ )}
{error && (
', () => {
let wrapper;
let history;
- beforeEach(async () => {
- await act(async () => {
- history = createMemoryHistory({
- initialEntries: ['/inventories/inventory/1/groups/1/details'],
- });
- wrapper = mountWithContexts(
-
-
- ,
- {
- context: {
- router: {
- history,
- route: {
- location: history.location,
- match: { params: { id: 1 } },
+
+ describe('User has full permissions', () => {
+ beforeEach(async () => {
+ await act(async () => {
+ history = createMemoryHistory({
+ initialEntries: ['/inventories/inventory/1/groups/1/details'],
+ });
+ wrapper = mountWithContexts(
+
+
+ ,
+ {
+ context: {
+ router: {
+ history,
+ route: {
+ location: history.location,
+ match: { params: { id: 1 } },
+ },
},
},
- },
- }
+ }
+ );
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ });
+ });
+ afterEach(() => {
+ wrapper.unmount();
+ jest.clearAllMocks();
+ });
+ test('InventoryGroupDetail renders successfully', () => {
+ expect(wrapper.length).toBe(1);
+ });
+
+ test('should open delete modal and then call api to delete the group', async () => {
+ await act(async () => {
+ wrapper.find('button[aria-label="Delete"]').simulate('click');
+ });
+ await waitForElement(wrapper, 'Modal', el => el.length === 1);
+ expect(wrapper.find('Modal').length).toBe(1);
+ await act(async () => {
+ wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
+ });
+ wrapper.update();
+ expect(
+ wrapper.find('Button[aria-label="Confirm Delete"]').prop('isDisabled')
+ ).toBe(false);
+ await act(() =>
+ wrapper.find('Button[aria-label="Confirm Delete"]').prop('onClick')()
);
- await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ expect(GroupsAPI.destroy).toBeCalledWith(1);
+ });
+
+ test('should navigate user to edit form on edit button click', async () => {
+ wrapper.find('button[aria-label="Edit"]').simulate('click');
+ expect(history.location.pathname).toEqual(
+ '/inventories/inventory/1/groups/1/edit'
+ );
+ });
+
+ test('details should render with the proper values and action buttons shown', () => {
+ expect(wrapper.find('Detail[label="Name"]').prop('value')).toBe('Foo');
+ expect(wrapper.find('Detail[label="Description"]').prop('value')).toBe(
+ 'Bar'
+ );
+ expect(wrapper.find('Detail[label="Created"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Last Modified"]').length).toBe(1);
+ expect(wrapper.find('VariablesDetail').prop('value')).toBe('bizz: buzz');
+
+ expect(wrapper.find('button[aria-label="Edit"]').length).toBe(1);
+ expect(wrapper.find('button[aria-label="Delete"]').length).toBe(1);
});
});
- afterEach(() => {
- wrapper.unmount();
- jest.clearAllMocks();
- });
- test('InventoryGroupDetail renders successfully', () => {
- expect(wrapper.length).toBe(1);
- });
- test('should open delete modal and then call api to delete the group', async () => {
- await act(async () => {
- wrapper.find('button[aria-label="Delete"]').simulate('click');
+ describe('User has read-only permissions', () => {
+ test('should hide edit/delete buttons', async () => {
+ const readOnlyGroup = {
+ ...inventoryGroup,
+ summary_fields: {
+ ...inventoryGroup.summary_fields,
+ user_capabilities: {
+ delete: false,
+ edit: false,
+ },
+ },
+ };
+
+ await act(async () => {
+ history = createMemoryHistory({
+ initialEntries: ['/inventories/inventory/1/groups/1/details'],
+ });
+ wrapper = mountWithContexts(
+
+
+ ,
+ {
+ context: {
+ router: {
+ history,
+ route: {
+ location: history.location,
+ match: { params: { id: 1 } },
+ },
+ },
+ },
+ }
+ );
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ });
+
+ expect(wrapper.find('button[aria-label="Edit"]').length).toBe(0);
+ expect(wrapper.find('button[aria-label="Delete"]').length).toBe(0);
+
+ wrapper.unmount();
});
- await waitForElement(wrapper, 'Modal', el => el.length === 1);
- expect(wrapper.find('Modal').length).toBe(1);
- await act(async () => {
- wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
- });
- wrapper.update();
- expect(
- wrapper.find('Button[aria-label="Confirm Delete"]').prop('isDisabled')
- ).toBe(false);
- await act(() =>
- wrapper.find('Button[aria-label="Confirm Delete"]').prop('onClick')()
- );
- expect(GroupsAPI.destroy).toBeCalledWith(1);
- });
-
- test('should navigate user to edit form on edit button click', async () => {
- wrapper.find('button[aria-label="Edit"]').simulate('click');
- expect(history.location.pathname).toEqual(
- '/inventories/inventory/1/groups/1/edit'
- );
- });
-
- test('details should render with the proper values', () => {
- expect(wrapper.find('Detail[label="Name"]').prop('value')).toBe('Foo');
- expect(wrapper.find('Detail[label="Description"]').prop('value')).toBe(
- 'Bar'
- );
- expect(wrapper.find('Detail[label="Created"]').length).toBe(1);
- expect(wrapper.find('Detail[label="Last Modified"]').length).toBe(1);
- expect(wrapper.find('VariablesDetail').prop('value')).toBe('bizz: buzz');
});
});