Resolves disassociate button for instances

This commit is contained in:
Alex Corey
2021-12-06 11:32:12 -05:00
parent 5512b71e16
commit 41385261f3
8 changed files with 86 additions and 41 deletions

View File

@@ -33,18 +33,30 @@ function DisassociateButton({
} }
}, [isKebabified, isOpen, onKebabModalChange]); }, [isKebabified, isOpen, onKebabModalChange]);
function cannotDisassociate(item) { function cannotDisassociateAllOthers(item) {
return !item.summary_fields?.user_capabilities?.delete; return !item.summary_fields?.user_capabilities?.delete;
} }
function cannotDisassociateInstances(item) {
return item.node_type === 'control';
}
const cannotDisassociate = itemsToDisassociate.some(
(i) => i.type === 'instance'
)
? cannotDisassociateInstances
: cannotDisassociateAllOthers;
function renderTooltip() { function renderTooltip() {
if (verifyCannotDisassociate) { if (verifyCannotDisassociate) {
const itemsUnableToDisassociate = itemsToDisassociate const itemsUnableToDisassociate = itemsToDisassociate
.filter(cannotDisassociate) .filter(cannotDisassociate)
.map((item) => item.name) .map((item) => item.name ?? item.hostname)
.join(', '); .join(', ');
if (
if (itemsToDisassociate.some(cannotDisassociate)) { cannotDisassociate
? itemsToDisassociate.some(cannotDisassociateInstances)
: itemsToDisassociate.some(cannotDisassociateAllOthers)
) {
return ( return (
<div> <div>
{t`You do not have permission to disassociate the following: ${itemsUnableToDisassociate}`} {t`You do not have permission to disassociate the following: ${itemsUnableToDisassociate}`}
@@ -79,12 +91,17 @@ function DisassociateButton({
aria-label={t`disassociate`} aria-label={t`disassociate`}
isDisabled={isDisabled} isDisabled={isDisabled}
component="button" component="button"
ouiaId="disassociate-tooltip"
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
> >
{t`Disassociate`} {t`Disassociate`}
</DropdownItem> </DropdownItem>
) : ( ) : (
<Tooltip content={renderTooltip()} position="top"> <Tooltip
content={renderTooltip()}
ouiaId="disassociate-tooltip"
position="top"
>
<div> <div>
<Button <Button
ouiaId="disassociate-button" ouiaId="disassociate-button"

View File

@@ -108,5 +108,21 @@ describe('<DisassociateButton />', () => {
); );
expect(wrapper.find('button[disabled]')).toHaveLength(1); expect(wrapper.find('button[disabled]')).toHaveLength(1);
}); });
test('should disable button for control instance', () => {
const wrapper = mountWithContexts(
<DisassociateButton
onDisassociate={() => {}}
itemsToDelete={[
{
id: 1,
hostname: 'awx',
node_type: 'control',
},
]}
/>
);
expect(wrapper.find('button[disabled]')).toHaveLength(1);
});
}); });
}); });

View File

@@ -273,13 +273,15 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
{t`Health Check`} {t`Health Check`}
</Button> </Button>
</Tooltip> </Tooltip>
<DisassociateButton {me.is_superuser && instance.node_type !== 'control' && (
verifyCannotDisassociate={!me.is_superuser} <DisassociateButton
key="disassociate" verifyCannotDisassociate={!me.is_superuser}
onDisassociate={disassociateInstance} key="disassociate"
itemsToDisassociate={[instance]} onDisassociate={disassociateInstance}
modalTitle={t`Disassociate instance from instance group?`} itemsToDisassociate={[instance]}
/> modalTitle={t`Disassociate instance from instance group?`}
/>
)}
<InstanceToggle <InstanceToggle
css="display: inline-flex;" css="display: inline-flex;"
fetchInstances={fetchDetails} fetchInstances={fetchDetails}

View File

@@ -230,9 +230,9 @@ describe('<InstanceDetails/>', () => {
); );
}); });
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
expect( expect(wrapper.find("Button[ouiaId='disassociate-button']")).toHaveLength(
wrapper.find("Button[ouiaId='disassociate-button']").prop('isDisabled') 0
).toBe(true); );
expect( expect(
wrapper.find("Button[ouiaId='health-check-button']").prop('isDisabled') wrapper.find("Button[ouiaId='health-check-button']").prop('isDisabled')
).toBe(true); ).toBe(true);
@@ -412,7 +412,7 @@ describe('<InstanceDetails/>', () => {
}, },
}); });
jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({ jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({
me: { is_system_auditor: true }, me: { is_superuser: true },
})); }));
await act(async () => { await act(async () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(
@@ -463,7 +463,7 @@ describe('<InstanceDetails/>', () => {
}, },
}); });
jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({ jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({
me: { is_system_auditor: true }, me: { is_superuser: true },
})); }));
await act(async () => { await act(async () => {
wrapper = mountWithContexts( wrapper = mountWithContexts(
@@ -474,6 +474,7 @@ describe('<InstanceDetails/>', () => {
); );
}); });
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
await act(async () => await act(async () =>
wrapper.find('Button[ouiaId="disassociate-button"]').prop('onClick')() wrapper.find('Button[ouiaId="disassociate-button"]').prop('onClick')()
); );

View File

@@ -207,12 +207,12 @@ function InstanceList() {
] ]
: []), : []),
<DisassociateButton <DisassociateButton
verifyCannotDisassociate={false} verifyCannotDisassociate={selected.some(
(s) => s.node_type === 'control'
)}
key="disassociate" key="disassociate"
onDisassociate={handleDisassociate} onDisassociate={handleDisassociate}
itemsToDisassociate={selected.filter( itemsToDisassociate={selected}
(s) => s.node_type !== 'control'
)}
modalTitle={t`Disassociate instance from instance group?`} modalTitle={t`Disassociate instance from instance group?`}
/>, />,
<Tooltip <Tooltip

View File

@@ -41,6 +41,7 @@ const instances = [
jobs_running: 0, jobs_running: 0,
jobs_total: 68, jobs_total: 68,
cpu: 6, cpu: 6,
node_type: 'control',
memory: 2087469056, memory: 2087469056,
cpu_capacity: 24, cpu_capacity: 24,
mem_capacity: 1, mem_capacity: 1,
@@ -67,6 +68,7 @@ const instances = [
jobs_running: 0, jobs_running: 0,
jobs_total: 68, jobs_total: 68,
cpu: 6, cpu: 6,
node_type: 'hybrid',
memory: 2087469056, memory: 2087469056,
cpu_capacity: 24, cpu_capacity: 24,
mem_capacity: 1, mem_capacity: 1,
@@ -93,6 +95,7 @@ const instances = [
jobs_running: 0, jobs_running: 0,
jobs_total: 68, jobs_total: 68,
cpu: 6, cpu: 6,
node_type: 'execution',
memory: 2087469056, memory: 2087469056,
cpu_capacity: 24, cpu_capacity: 24,
mem_capacity: 1, mem_capacity: 1,
@@ -197,4 +200,29 @@ describe('<InstanceList/>', () => {
wrapper.update(); wrapper.update();
expect(wrapper.find('AlertModal')).toHaveLength(1); expect(wrapper.find('AlertModal')).toHaveLength(1);
}); });
test('should disable disassociate button', async () => {
expect(
wrapper.find('Button[ouiaId="disassociate-button"]').prop('isDisabled')
).toBe(true);
await act(async () =>
wrapper.find('DataListToolbar').prop('onSelectAll')(instances)
);
wrapper.update();
expect(
wrapper.find('Button[ouiaId="disassociate-button"]').prop('isDisabled')
).toBe(true);
await act(async () =>
wrapper
.find('Tr#instance-row-1')
.find('SelectColumn[aria-label="Select row 0"]')
.prop('onSelect')(false)
);
wrapper.update();
expect(
wrapper.find('Button[ouiaId="disassociate-button"]').prop('isDisabled')
).toBe(false);
});
}); });

View File

@@ -121,9 +121,8 @@ function InstanceListItem({
<Td <Td
select={{ select={{
rowIndex, rowIndex,
isSelected: isSelected && instance.node_type !== 'control', isSelected,
onSelect, onSelect,
disable: instance.node_type === 'control',
}} }}
dataLabel={t`Selected`} dataLabel={t`Selected`}
/> />

View File

@@ -201,24 +201,6 @@ describe('<InstanceListItem/>', () => {
expect(wrapper.find('Td').at(1).prop('select').onSelect).toEqual(onSelect); expect(wrapper.find('Td').at(1).prop('select').onSelect).toEqual(onSelect);
}); });
test('should disable checkbox', async () => {
const onSelect = jest.fn();
await act(async () => {
wrapper = mountWithContexts(
<table>
<tbody>
<InstanceListItem
instance={instance[1]}
onSelect={onSelect}
fetchInstances={() => {}}
/>
</tbody>
</table>
);
});
expect(wrapper.find('Td').at(1).prop('select').disable).toEqual(true);
});
test('should display instance toggle', () => { test('should display instance toggle', () => {
expect(wrapper.find('InstanceToggle').length).toBe(1); expect(wrapper.find('InstanceToggle').length).toBe(1);
}); });