mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Adds an execution environment step to the ad hoc commands
This commit is contained in:
parent
1e7b7d1a30
commit
e6bde23aea
@ -12,10 +12,9 @@ import AlertModal from '../AlertModal';
|
||||
import ErrorDetail from '../ErrorDetail';
|
||||
import AdHocCommandsWizard from './AdHocCommandsWizard';
|
||||
import { KebabifiedContext } from '../../contexts/Kebabified';
|
||||
import ContentLoading from '../ContentLoading';
|
||||
import ContentError from '../ContentError';
|
||||
|
||||
function AdHocCommands({ adHocItems, i18n, hasListItems }) {
|
||||
function AdHocCommands({ adHocItems, i18n, hasListItems, onLaunchLoading }) {
|
||||
const history = useHistory();
|
||||
const { id } = useParams();
|
||||
|
||||
@ -76,19 +75,20 @@ function AdHocCommands({ adHocItems, i18n, hasListItems }) {
|
||||
);
|
||||
|
||||
const handleSubmit = async values => {
|
||||
const { credential, ...remainingValues } = values;
|
||||
const { credential, execution_environment, ...remainingValues } = values;
|
||||
const newCredential = credential[0].id;
|
||||
|
||||
const manipulatedValues = {
|
||||
credential: newCredential,
|
||||
execution_environment: execution_environment[0]?.id,
|
||||
...remainingValues,
|
||||
};
|
||||
await launchAdHocCommands(manipulatedValues);
|
||||
};
|
||||
|
||||
if (isLaunchLoading) {
|
||||
return <ContentLoading />;
|
||||
}
|
||||
useEffect(() => onLaunchLoading(isLaunchLoading), [
|
||||
isLaunchLoading,
|
||||
onLaunchLoading,
|
||||
]);
|
||||
|
||||
if (error && isWizardOpen) {
|
||||
return (
|
||||
|
||||
@ -4,12 +4,19 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
import { CredentialTypesAPI, InventoriesAPI, CredentialsAPI } from '../../api';
|
||||
import {
|
||||
CredentialTypesAPI,
|
||||
InventoriesAPI,
|
||||
CredentialsAPI,
|
||||
ExecutionEnvironmentsAPI,
|
||||
} from '../../api';
|
||||
import AdHocCommands from './AdHocCommands';
|
||||
|
||||
jest.mock('../../api/models/CredentialTypes');
|
||||
jest.mock('../../api/models/Inventories');
|
||||
jest.mock('../../api/models/Credentials');
|
||||
jest.mock('../../api/models/ExecutionEnvironments');
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useParams: () => ({
|
||||
@ -51,6 +58,15 @@ describe('<AdHocCommands />', () => {
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: { count: 1, results: [{ id: 1, name: 'cred' }] },
|
||||
});
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{ id: 1, name: 'EE1 1', url: 'wwww.google.com' },
|
||||
{ id: 2, name: 'EE2', url: 'wwww.google.com' },
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
let wrapper;
|
||||
afterEach(() => {
|
||||
@ -61,7 +77,11 @@ describe('<AdHocCommands />', () => {
|
||||
test('mounts successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('AdHocCommands').length).toBe(1);
|
||||
@ -86,9 +106,22 @@ describe('<AdHocCommands />', () => {
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: { results: [{ id: 1 }] },
|
||||
});
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{ id: 1, name: 'EE1 1', url: 'wwww.google.com' },
|
||||
{ id: 2, name: 'EE2', url: 'wwww.google.com' },
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await act(async () =>
|
||||
@ -108,9 +141,22 @@ describe('<AdHocCommands />', () => {
|
||||
count: 5,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{ id: 1, name: 'EE1 1', url: 'wwww.google.com' },
|
||||
{ id: 2, name: 'EE2', url: 'wwww.google.com' },
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -147,8 +193,27 @@ describe('<AdHocCommands />', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
|
||||
// second step of wizard
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-2"]')
|
||||
.simulate('change', { target: { checked: true } });
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[label="EE2"]').prop('isSelected')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
// third step of wizard
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-4"]')
|
||||
@ -176,6 +241,7 @@ describe('<AdHocCommands />', () => {
|
||||
limit: 'Inventory 1 Org 0, Inventory 2 Org 0',
|
||||
module_name: 'command',
|
||||
verbosity: 1,
|
||||
execution_environment: 2,
|
||||
});
|
||||
});
|
||||
|
||||
@ -202,13 +268,21 @@ describe('<AdHocCommands />', () => {
|
||||
['foo', 'foo'],
|
||||
],
|
||||
},
|
||||
verbosity: { choices: [[1], [2]] },
|
||||
verbosity: {
|
||||
choices: [[1], [2]],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
CredentialTypesAPI.read.mockResolvedValue({
|
||||
data: { results: [{ id: 1 }] },
|
||||
data: {
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
CredentialsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
@ -216,9 +290,30 @@ describe('<AdHocCommands />', () => {
|
||||
count: 5,
|
||||
},
|
||||
});
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'EE1 1',
|
||||
url: 'wwww.google.com',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'EE2',
|
||||
url: 'wwww.google.com',
|
||||
},
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@ -240,7 +335,10 @@ describe('<AdHocCommands />', () => {
|
||||
'command'
|
||||
);
|
||||
wrapper.find('input#module_args').simulate('change', {
|
||||
target: { value: 'foo', name: 'module_args' },
|
||||
target: {
|
||||
value: 'foo',
|
||||
name: 'module_args',
|
||||
},
|
||||
});
|
||||
wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1);
|
||||
});
|
||||
@ -259,10 +357,36 @@ describe('<AdHocCommands />', () => {
|
||||
|
||||
// second step of wizard
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-2"]')
|
||||
.simulate('change', {
|
||||
target: {
|
||||
checked: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[label="EE2"]').prop('isSelected')
|
||||
).toBe(true);
|
||||
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
// third step of wizard
|
||||
await waitForElement(wrapper, 'ContentEmpty', el => el.length === 0);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-4"]')
|
||||
.simulate('change', { target: { checked: true } });
|
||||
.simulate('change', {
|
||||
target: {
|
||||
checked: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
@ -291,7 +415,11 @@ describe('<AdHocCommands />', () => {
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
@ -312,7 +440,11 @@ describe('<AdHocCommands />', () => {
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems={false} />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems={false}
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||
@ -335,7 +467,11 @@ describe('<AdHocCommands />', () => {
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<AdHocCommands adHocItems={adHocItems} hasListItems />
|
||||
<AdHocCommands
|
||||
adHocItems={adHocItems}
|
||||
hasListItems
|
||||
onLaunchLoading={() => jest.fn()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
await act(async () => wrapper.find('button').prop('onClick')());
|
||||
|
||||
@ -10,6 +10,7 @@ import styled from 'styled-components';
|
||||
import Wizard from '../Wizard';
|
||||
import AdHocCredentialStep from './AdHocCredentialStep';
|
||||
import AdHocDetailsStep from './AdHocDetailsStep';
|
||||
import AdHocExecutionEnvironmentStep from './AdHocExecutionEnvironmentStep';
|
||||
|
||||
const AlertText = styled.div`
|
||||
color: var(--pf-global--danger-color--200);
|
||||
@ -81,6 +82,14 @@ function AdHocCommandsWizard({
|
||||
{
|
||||
id: 2,
|
||||
key: 2,
|
||||
name: t`Execution Environment`,
|
||||
component: <AdHocExecutionEnvironmentStep />,
|
||||
enableNext: true,
|
||||
canJumpTo: currentStepId >= 2,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
key: 3,
|
||||
name: i18n._(t`Machine credential`),
|
||||
component: (
|
||||
<AdHocCredentialStep
|
||||
@ -128,6 +137,7 @@ const FormikApp = withFormik({
|
||||
module_name: '',
|
||||
extra_vars: '---',
|
||||
job_type: 'run',
|
||||
execution_environment: '',
|
||||
};
|
||||
},
|
||||
})(AdHocCommandsWizard);
|
||||
|
||||
@ -4,12 +4,14 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
import { CredentialsAPI } from '../../api';
|
||||
import { CredentialsAPI, ExecutionEnvironmentsAPI } from '../../api';
|
||||
import AdHocCommandsWizard from './AdHocCommandsWizard';
|
||||
|
||||
jest.mock('../../api/models/CredentialTypes');
|
||||
jest.mock('../../api/models/Inventories');
|
||||
jest.mock('../../api/models/Credentials');
|
||||
jest.mock('../../api/models/ExecutionEnvironments');
|
||||
|
||||
const verbosityOptions = [
|
||||
{ value: '0', key: '0', label: '0 (Normal)' },
|
||||
{ value: '1', key: '1', label: '1 (Verbose)' },
|
||||
@ -97,6 +99,15 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
wrapper.update();
|
||||
});
|
||||
test('launch button should become active', async () => {
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{ id: 1, name: 'EE 1', url: '' },
|
||||
{ id: 2, name: 'EE 2', url: '' },
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
CredentialsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
@ -127,10 +138,40 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
// step 2
|
||||
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-1"]')
|
||||
.simulate('change', { target: { checked: true } });
|
||||
});
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('CheckboxListItem[label="EE 1"]').prop('isSelected')
|
||||
).toBe(true);
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(
|
||||
false
|
||||
);
|
||||
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
// step 3
|
||||
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('input[aria-labelledby="check-action-item-1"]')
|
||||
@ -150,8 +191,21 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
|
||||
expect(onLaunch).toHaveBeenCalled();
|
||||
expect(onLaunch).toHaveBeenCalledWith({
|
||||
become_enabled: '',
|
||||
credential: [{ id: 1, name: 'Cred 1', url: '' }],
|
||||
diff_mode: false,
|
||||
execution_environment: [{ id: 1, name: 'EE 1', url: '' }],
|
||||
extra_vars: '---',
|
||||
forks: 0,
|
||||
job_type: 'run',
|
||||
limit: 'Inventory 1, Inventory 2, inventory 3',
|
||||
module_args: 'foo',
|
||||
module_name: 'command',
|
||||
verbosity: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('should show error in navigation bar', async () => {
|
||||
await waitForElement(wrapper, 'WizardNavItem', el => el.length > 0);
|
||||
|
||||
@ -201,6 +255,12 @@ describe('<AdHocCommandsWizard/>', () => {
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
await act(async () =>
|
||||
wrapper.find('Button[type="submit"]').prop('onClick')()
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
expect(wrapper.find('ContentError').length).toBe(1);
|
||||
});
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { Formik } from 'formik';
|
||||
import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
import { ExecutionEnvironmentsAPI } from '../../api';
|
||||
import AdHocExecutionEnvironmentStep from './AdHocExecutionEnvironmentStep';
|
||||
|
||||
jest.mock('../../api/models/ExecutionEnvironments');
|
||||
|
||||
describe('<AdHocExecutionEnvironmentStep />', () => {
|
||||
let wrapper;
|
||||
beforeEach(async () => {
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue({
|
||||
data: {
|
||||
results: [
|
||||
{ id: 1, name: 'EE1 1', url: 'wwww.google.com' },
|
||||
{ id: 2, name: 'EE2', url: 'wwww.google.com' },
|
||||
],
|
||||
count: 2,
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Formik>
|
||||
<AdHocExecutionEnvironmentStep />
|
||||
</Formik>
|
||||
);
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('should mount properly', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
});
|
||||
|
||||
test('should call api', async () => {
|
||||
await waitForElement(wrapper, 'OptionsList', el => el.length > 0);
|
||||
expect(ExecutionEnvironmentsAPI.read).toHaveBeenCalled();
|
||||
expect(wrapper.find('CheckboxListItem').length).toBe(2);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,108 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
import { useField } from 'formik';
|
||||
import { Form, FormGroup } from '@patternfly/react-core';
|
||||
import { ExecutionEnvironmentsAPI } from '../../api';
|
||||
import Popover from '../Popover';
|
||||
|
||||
import { parseQueryString, getQSConfig } from '../../util/qs';
|
||||
import useRequest from '../../util/useRequest';
|
||||
import ContentError from '../ContentError';
|
||||
import ContentLoading from '../ContentLoading';
|
||||
import OptionsList from '../OptionsList';
|
||||
|
||||
const QS_CONFIG = getQSConfig('execution_environemts', {
|
||||
page: 1,
|
||||
page_size: 5,
|
||||
order_by: 'name',
|
||||
});
|
||||
function AdHocExecutionEnvironmentStep() {
|
||||
const history = useHistory();
|
||||
const [executionEnvironmentField, , executionEnvironmentHelpers] = useField(
|
||||
'execution_environment'
|
||||
);
|
||||
const {
|
||||
error,
|
||||
isLoading,
|
||||
request: fetchExecutionEnvironments,
|
||||
result: { executionEnvironments, executionEnvironmentsCount },
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const params = parseQueryString(QS_CONFIG, history.location.search);
|
||||
|
||||
const {
|
||||
data: { results, count },
|
||||
} = await ExecutionEnvironmentsAPI.read(params);
|
||||
|
||||
return {
|
||||
executionEnvironments: results,
|
||||
executionEnvironmentsCount: count,
|
||||
};
|
||||
}, [history.location.search]),
|
||||
{ executionEnvironments: [], executionEnvironmentsCount: 0 }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchExecutionEnvironments();
|
||||
}, [fetchExecutionEnvironments]);
|
||||
|
||||
if (error) {
|
||||
return <ContentError error={error} />;
|
||||
}
|
||||
if (isLoading) {
|
||||
return <ContentLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<FormGroup
|
||||
fieldId="execution_enviroment"
|
||||
label={t`Execution Environments`}
|
||||
aria-label={t`Execution Environments`}
|
||||
labelIcon={
|
||||
<Popover
|
||||
content={t`Select the Execution Environment you want this command to run inside`}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<OptionsList
|
||||
value={executionEnvironmentField.value || []}
|
||||
options={executionEnvironments}
|
||||
optionCount={executionEnvironmentsCount}
|
||||
header={t`Execution Environments`}
|
||||
qsConfig={QS_CONFIG}
|
||||
searchColumns={[
|
||||
{
|
||||
name: t`Name`,
|
||||
key: 'name',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
name: t`Created By (Username)`,
|
||||
key: 'created_by__username',
|
||||
},
|
||||
{
|
||||
name: t`Modified By (Username)`,
|
||||
key: 'modified_by__username',
|
||||
},
|
||||
]}
|
||||
sortColumns={[
|
||||
{
|
||||
name: t`Name`,
|
||||
key: 'name',
|
||||
},
|
||||
]}
|
||||
name="execution_environment"
|
||||
selectItem={value => {
|
||||
executionEnvironmentHelpers.setValue([value]);
|
||||
}}
|
||||
deselectItem={() => {
|
||||
executionEnvironmentHelpers.setValue([]);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
export default AdHocExecutionEnvironmentStep;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -92,11 +92,11 @@ describe('<InventoryDetail />', () => {
|
||||
expectDetailToMatch(wrapper, 'Type', 'Inventory');
|
||||
const org = wrapper.find('Detail[label="Organization"]');
|
||||
expect(org.prop('value')).toMatchInlineSnapshot(`
|
||||
<Link
|
||||
<ForwardRef
|
||||
to="/organizations/1/details"
|
||||
>
|
||||
The Organization
|
||||
</Link>
|
||||
</ForwardRef>
|
||||
`);
|
||||
const vars = wrapper.find('VariablesDetail');
|
||||
expect(vars).toHaveLength(1);
|
||||
|
||||
@ -28,6 +28,7 @@ const QS_CONFIG = getQSConfig('host', {
|
||||
});
|
||||
|
||||
function InventoryGroupHostList({ i18n }) {
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const { id: inventoryId, groupId } = useParams();
|
||||
const location = useLocation();
|
||||
@ -172,7 +173,9 @@ function InventoryGroupHostList({ i18n }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading || isDisassociateLoading}
|
||||
hasContentLoading={
|
||||
isLoading || isDisassociateLoading || isAdHocLaunchLoading
|
||||
}
|
||||
items={hosts}
|
||||
itemCount={hostCount}
|
||||
pluralizedItemName={i18n._(t`Hosts`)}
|
||||
@ -215,6 +218,7 @@ function InventoryGroupHostList({ i18n }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={hostCount > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
<DisassociateButton
|
||||
key="disassociate"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -30,6 +30,7 @@ function cannotDelete(item) {
|
||||
function InventoryGroupsList({ i18n }) {
|
||||
const location = useLocation();
|
||||
const { id: inventoryId } = useParams();
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
|
||||
const {
|
||||
result: {
|
||||
@ -107,7 +108,7 @@ function InventoryGroupsList({ i18n }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading}
|
||||
hasContentLoading={isLoading || isAdHocLaunchLoading}
|
||||
items={groups}
|
||||
itemCount={groupCount}
|
||||
qsConfig={QS_CONFIG}
|
||||
@ -174,6 +175,7 @@ function InventoryGroupsList({ i18n }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={groupCount > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
<Tooltip content={renderTooltip()} position="top" key="delete">
|
||||
<InventoryGroupsDeleteModal
|
||||
|
||||
@ -28,6 +28,7 @@ const QS_CONFIG = getQSConfig('group', {
|
||||
|
||||
function InventoryHostGroupsList({ i18n }) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
const { hostId, id: invId } = useParams();
|
||||
const { search } = useLocation();
|
||||
|
||||
@ -147,7 +148,9 @@ function InventoryHostGroupsList({ i18n }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading || isDisassociateLoading}
|
||||
hasContentLoading={
|
||||
isLoading || isDisassociateLoading || isAdHocLaunchLoading
|
||||
}
|
||||
items={groups}
|
||||
itemCount={itemCount}
|
||||
qsConfig={QS_CONFIG}
|
||||
@ -205,6 +208,7 @@ function InventoryHostGroupsList({ i18n }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={itemCount > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
<DisassociateButton
|
||||
key="disassociate"
|
||||
|
||||
@ -23,6 +23,7 @@ const QS_CONFIG = getQSConfig('host', {
|
||||
|
||||
function InventoryHostList({ i18n }) {
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
const { id } = useParams();
|
||||
const { search } = useLocation();
|
||||
|
||||
@ -106,7 +107,7 @@ function InventoryHostList({ i18n }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading || isDeleteLoading}
|
||||
hasContentLoading={isLoading || isDeleteLoading || isAdHocLaunchLoading}
|
||||
items={hosts}
|
||||
itemCount={hostCount}
|
||||
pluralizedItemName={i18n._(t`Hosts`)}
|
||||
@ -152,6 +153,7 @@ function InventoryHostList({ i18n }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={hostCount > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
<ToolbarDeleteButton
|
||||
key="delete"
|
||||
|
||||
@ -26,6 +26,7 @@ const QS_CONFIG = getQSConfig('group', {
|
||||
});
|
||||
function InventoryRelatedGroupList({ i18n }) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
const [associateError, setAssociateError] = useState(null);
|
||||
const [disassociateError, setDisassociateError] = useState(null);
|
||||
const { id: inventoryId, groupId } = useParams();
|
||||
@ -154,7 +155,7 @@ function InventoryRelatedGroupList({ i18n }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading}
|
||||
hasContentLoading={isLoading || isAdHocLaunchLoading}
|
||||
items={groups}
|
||||
itemCount={itemCount}
|
||||
pluralizedItemName={i18n._(t`Related Groups`)}
|
||||
@ -197,6 +198,7 @@ function InventoryRelatedGroupList({ i18n }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={itemCount > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
<DisassociateButton
|
||||
key="disassociate"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
@ -20,7 +20,7 @@ const QS_CONFIG = getQSConfig('host', {
|
||||
|
||||
function SmartInventoryHostList({ i18n, inventory }) {
|
||||
const location = useLocation();
|
||||
|
||||
const [isAdHocLaunchLoading, setIsAdHocLaunchLoading] = useState(false);
|
||||
const {
|
||||
result: { hosts, count },
|
||||
error: contentError,
|
||||
@ -56,7 +56,7 @@ function SmartInventoryHostList({ i18n, inventory }) {
|
||||
<>
|
||||
<PaginatedDataList
|
||||
contentError={contentError}
|
||||
hasContentLoading={isLoading}
|
||||
hasContentLoading={isLoading || isAdHocLaunchLoading}
|
||||
items={hosts}
|
||||
itemCount={count}
|
||||
pluralizedItemName={i18n._(t`Hosts`)}
|
||||
@ -98,6 +98,7 @@ function SmartInventoryHostList({ i18n, inventory }) {
|
||||
<AdHocCommands
|
||||
adHocItems={selected}
|
||||
hasListItems={count > 0}
|
||||
onLaunchLoading={setIsAdHocLaunchLoading}
|
||||
/>,
|
||||
]
|
||||
: []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user