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:
softwarefactory-project-zuul[bot] 2020-11-30 13:37:35 +00:00 committed by GitHub
commit 170e64070b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 78 deletions

View File

@ -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 }) {
/>
</DetailList>
<CardActionsRow>
<Button
variant="primary"
aria-label={i18n._(t`Edit`)}
onClick={() =>
history.push(
`/inventories/inventory/${params.id}/groups/${params.groupId}/edit`
)
}
>
{i18n._(t`Edit`)}
</Button>
<InventoryGroupsDeleteModal
groups={[inventoryGroup]}
isDisabled={false}
onAfterDelete={() =>
history.push(`/inventories/inventory/${params.id}/groups`)
}
/>
{user_capabilities?.edit && (
<Button
variant="primary"
aria-label={i18n._(t`Edit`)}
onClick={() =>
history.push(
`/inventories/inventory/${params.id}/groups/${params.groupId}/edit`
)
}
>
{i18n._(t`Edit`)}
</Button>
)}
{user_capabilities?.delete && (
<InventoryGroupsDeleteModal
groups={[inventoryGroup]}
isDisabled={false}
onAfterDelete={() =>
history.push(`/inventories/inventory/${params.id}/groups`)
}
/>
)}
</CardActionsRow>
{error && (
<AlertModal

View File

@ -27,77 +27,130 @@ const inventoryGroup = {
username: 'Bond',
id: 14,
},
user_capabilities: {
delete: true,
edit: true,
},
},
};
describe('<InventoryGroupDetail />', () => {
let wrapper;
let history;
beforeEach(async () => {
await act(async () => {
history = createMemoryHistory({
initialEntries: ['/inventories/inventory/1/groups/1/details'],
});
wrapper = mountWithContexts(
<Route path="/inventories/inventory/:id/groups/:groupId">
<InventoryGroupDetail inventoryGroup={inventoryGroup} />
</Route>,
{
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(
<Route path="/inventories/inventory/:id/groups/:groupId">
<InventoryGroupDetail inventoryGroup={inventoryGroup} />
</Route>,
{
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(
<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');
});
});