diff --git a/awx/ui/src/components/DisassociateButton/DisassociateButton.js b/awx/ui/src/components/DisassociateButton/DisassociateButton.js index e2b3275b8d..9e6a4e81c6 100644 --- a/awx/ui/src/components/DisassociateButton/DisassociateButton.js +++ b/awx/ui/src/components/DisassociateButton/DisassociateButton.js @@ -33,18 +33,30 @@ function DisassociateButton({ } }, [isKebabified, isOpen, onKebabModalChange]); - function cannotDisassociate(item) { + function cannotDisassociateAllOthers(item) { 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() { if (verifyCannotDisassociate) { const itemsUnableToDisassociate = itemsToDisassociate .filter(cannotDisassociate) - .map((item) => item.name) + .map((item) => item.name ?? item.hostname) .join(', '); - - if (itemsToDisassociate.some(cannotDisassociate)) { + if ( + cannotDisassociate + ? itemsToDisassociate.some(cannotDisassociateInstances) + : itemsToDisassociate.some(cannotDisassociateAllOthers) + ) { return (
{t`You do not have permission to disassociate the following: ${itemsUnableToDisassociate}`} @@ -79,12 +91,17 @@ function DisassociateButton({ aria-label={t`disassociate`} isDisabled={isDisabled} component="button" + ouiaId="disassociate-tooltip" onClick={() => setIsOpen(true)} > {t`Disassociate`} ) : ( - +
- + {me.is_superuser && instance.node_type !== 'control' && ( + + )} ', () => { ); }); await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect( - wrapper.find("Button[ouiaId='disassociate-button']").prop('isDisabled') - ).toBe(true); + expect(wrapper.find("Button[ouiaId='disassociate-button']")).toHaveLength( + 0 + ); expect( wrapper.find("Button[ouiaId='health-check-button']").prop('isDisabled') ).toBe(true); @@ -412,7 +412,7 @@ describe('', () => { }, }); jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({ - me: { is_system_auditor: true }, + me: { is_superuser: true }, })); await act(async () => { wrapper = mountWithContexts( @@ -463,7 +463,7 @@ describe('', () => { }, }); jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({ - me: { is_system_auditor: true }, + me: { is_superuser: true }, })); await act(async () => { wrapper = mountWithContexts( @@ -474,6 +474,7 @@ describe('', () => { ); }); await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); + await act(async () => wrapper.find('Button[ouiaId="disassociate-button"]').prop('onClick')() ); diff --git a/awx/ui/src/screens/InstanceGroup/Instances/InstanceList.js b/awx/ui/src/screens/InstanceGroup/Instances/InstanceList.js index bf7b8d5f10..3d2f3bac93 100644 --- a/awx/ui/src/screens/InstanceGroup/Instances/InstanceList.js +++ b/awx/ui/src/screens/InstanceGroup/Instances/InstanceList.js @@ -207,12 +207,12 @@ function InstanceList() { ] : []), s.node_type === 'control' + )} key="disassociate" onDisassociate={handleDisassociate} - itemsToDisassociate={selected.filter( - (s) => s.node_type !== 'control' - )} + itemsToDisassociate={selected} modalTitle={t`Disassociate instance from instance group?`} />, ', () => { wrapper.update(); 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); + }); }); diff --git a/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.js b/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.js index e666e68ab0..8f43814db4 100644 --- a/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.js +++ b/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.js @@ -121,9 +121,8 @@ function InstanceListItem({ diff --git a/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.test.js b/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.test.js index a549923d0c..77c7a92797 100644 --- a/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.test.js +++ b/awx/ui/src/screens/InstanceGroup/Instances/InstanceListItem.test.js @@ -201,24 +201,6 @@ describe('', () => { 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( - - - {}} - /> - -
- ); - }); - expect(wrapper.find('Td').at(1).prop('select').disable).toEqual(true); - }); - test('should display instance toggle', () => { expect(wrapper.find('InstanceToggle').length).toBe(1); });