mirror of
https://github.com/ansible/awx.git
synced 2026-03-02 17:28:51 -03:30
Merge pull request #8659 from mabashian/7989-group-action-buttons
Hide edit/delete buttons on group details view for users that don't have permissions Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -17,7 +17,7 @@ import InventoryGroupsDeleteModal from '../shared/InventoryGroupsDeleteModal';
|
|||||||
|
|
||||||
function InventoryGroupDetail({ i18n, inventoryGroup }) {
|
function InventoryGroupDetail({ i18n, inventoryGroup }) {
|
||||||
const {
|
const {
|
||||||
summary_fields: { created_by, modified_by },
|
summary_fields: { created_by, modified_by, user_capabilities },
|
||||||
created,
|
created,
|
||||||
modified,
|
modified,
|
||||||
name,
|
name,
|
||||||
@@ -54,24 +54,28 @@ function InventoryGroupDetail({ i18n, inventoryGroup }) {
|
|||||||
/>
|
/>
|
||||||
</DetailList>
|
</DetailList>
|
||||||
<CardActionsRow>
|
<CardActionsRow>
|
||||||
<Button
|
{user_capabilities?.edit && (
|
||||||
variant="primary"
|
<Button
|
||||||
aria-label={i18n._(t`Edit`)}
|
variant="primary"
|
||||||
onClick={() =>
|
aria-label={i18n._(t`Edit`)}
|
||||||
history.push(
|
onClick={() =>
|
||||||
`/inventories/inventory/${params.id}/groups/${params.groupId}/edit`
|
history.push(
|
||||||
)
|
`/inventories/inventory/${params.id}/groups/${params.groupId}/edit`
|
||||||
}
|
)
|
||||||
>
|
}
|
||||||
{i18n._(t`Edit`)}
|
>
|
||||||
</Button>
|
{i18n._(t`Edit`)}
|
||||||
<InventoryGroupsDeleteModal
|
</Button>
|
||||||
groups={[inventoryGroup]}
|
)}
|
||||||
isDisabled={false}
|
{user_capabilities?.delete && (
|
||||||
onAfterDelete={() =>
|
<InventoryGroupsDeleteModal
|
||||||
history.push(`/inventories/inventory/${params.id}/groups`)
|
groups={[inventoryGroup]}
|
||||||
}
|
isDisabled={false}
|
||||||
/>
|
onAfterDelete={() =>
|
||||||
|
history.push(`/inventories/inventory/${params.id}/groups`)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</CardActionsRow>
|
</CardActionsRow>
|
||||||
{error && (
|
{error && (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
|
|||||||
@@ -27,77 +27,130 @@ const inventoryGroup = {
|
|||||||
username: 'Bond',
|
username: 'Bond',
|
||||||
id: 14,
|
id: 14,
|
||||||
},
|
},
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
edit: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('<InventoryGroupDetail />', () => {
|
describe('<InventoryGroupDetail />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
let history;
|
let history;
|
||||||
beforeEach(async () => {
|
|
||||||
await act(async () => {
|
describe('User has full permissions', () => {
|
||||||
history = createMemoryHistory({
|
beforeEach(async () => {
|
||||||
initialEntries: ['/inventories/inventory/1/groups/1/details'],
|
await act(async () => {
|
||||||
});
|
history = createMemoryHistory({
|
||||||
wrapper = mountWithContexts(
|
initialEntries: ['/inventories/inventory/1/groups/1/details'],
|
||||||
<Route path="/inventories/inventory/:id/groups/:groupId">
|
});
|
||||||
<InventoryGroupDetail inventoryGroup={inventoryGroup} />
|
wrapper = mountWithContexts(
|
||||||
</Route>,
|
<Route path="/inventories/inventory/:id/groups/:groupId">
|
||||||
{
|
<InventoryGroupDetail inventoryGroup={inventoryGroup} />
|
||||||
context: {
|
</Route>,
|
||||||
router: {
|
{
|
||||||
history,
|
context: {
|
||||||
route: {
|
router: {
|
||||||
location: history.location,
|
history,
|
||||||
match: { params: { id: 1 } },
|
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 () => {
|
describe('User has read-only permissions', () => {
|
||||||
await act(async () => {
|
test('should hide edit/delete buttons', async () => {
|
||||||
wrapper.find('button[aria-label="Delete"]').simulate('click');
|
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(
|
||||||
|
<Route path="/inventories/inventory/:id/groups/:groupId">
|
||||||
|
<InventoryGroupDetail inventoryGroup={readOnlyGroup} />
|
||||||
|
</Route>,
|
||||||
|
{
|
||||||
|
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');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user