mirror of
https://github.com/ansible/awx.git
synced 2026-05-09 02:17:37 -02:30
Resolves disassociate button for instances
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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')()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user