foreman_remote_execution 7.2.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby_ci.yml +2 -2
- data/app/controllers/ui_job_wizard_controller.rb +15 -0
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/models/job_invocation.rb +2 -4
- data/app/models/job_invocation_composer.rb +5 -2
- data/app/models/remote_execution_provider.rb +1 -1
- data/app/views/templates/script/package_action.erb +8 -3
- data/config/routes.rb +3 -1
- data/lib/foreman_remote_execution/engine.rb +5 -5
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/functional/api/v2/job_invocations_controller_test.rb +8 -0
- data/test/helpers/remote_execution_helper_test.rb +4 -0
- data/test/unit/job_invocation_report_template_test.rb +1 -1
- data/test/unit/job_invocation_test.rb +1 -2
- data/test/unit/remote_execution_provider_test.rb +0 -22
- data/webpack/JobWizard/JobWizard.js +154 -20
- data/webpack/JobWizard/JobWizard.scss +43 -1
- data/webpack/JobWizard/JobWizardConstants.js +11 -1
- data/webpack/JobWizard/JobWizardPageRerun.js +112 -0
- data/webpack/JobWizard/__tests__/JobWizardPageRerun.test.js +79 -0
- data/webpack/JobWizard/__tests__/fixtures.js +73 -0
- data/webpack/JobWizard/__tests__/integration.test.js +17 -3
- data/webpack/JobWizard/autofill.js +8 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +36 -17
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -3
- data/webpack/JobWizard/steps/ReviewDetails/index.js +1 -3
- data/webpack/JobWizard/steps/Schedule/PurposeField.js +1 -3
- data/webpack/JobWizard/steps/Schedule/QueryType.js +33 -40
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +55 -16
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +19 -56
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +1 -1
- data/webpack/JobWizard/steps/Schedule/ScheduleFuture.js +126 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js +287 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +88 -20
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +206 -186
- data/webpack/JobWizard/steps/form/DateTimePicker.js +23 -6
- data/webpack/JobWizard/steps/form/Formatter.js +7 -8
- data/webpack/JobWizard/submit.js +8 -3
- data/webpack/Routes/routes.js +8 -2
- data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +1 -0
- data/webpack/react_app/components/HostKebab/KebabItems.js +0 -1
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +0 -5
- data/webpack/react_app/components/RecentJobsCard/RecentJobsTable.js +59 -51
- data/webpack/react_app/extend/Fills.js +4 -4
- metadata +8 -6
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -106
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -32
- data/webpack/JobWizard/steps/Schedule/index.js +0 -178
@@ -0,0 +1,112 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import URI from 'urijs';
|
4
|
+
import { Alert, Title, Divider, Skeleton } from '@patternfly/react-core';
|
5
|
+
import { sprintf, translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
7
|
+
import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
|
8
|
+
import { STATUS } from 'foremanReact/constants';
|
9
|
+
import {
|
10
|
+
useForemanOrganization,
|
11
|
+
useForemanLocation,
|
12
|
+
} from 'foremanReact/Root/Context/ForemanContext';
|
13
|
+
import { JobWizard } from './JobWizard';
|
14
|
+
import { JOB_API_KEY } from './JobWizardConstants';
|
15
|
+
|
16
|
+
const JobWizardPageRerun = ({
|
17
|
+
match: {
|
18
|
+
params: { id },
|
19
|
+
},
|
20
|
+
location: { search },
|
21
|
+
}) => {
|
22
|
+
const uri = new URI(search);
|
23
|
+
const { failed_only: failedOnly } = uri.search(true);
|
24
|
+
const { response, status } = useAPI(
|
25
|
+
'get',
|
26
|
+
`/ui_job_wizard/job_invocation?id=${id}${
|
27
|
+
failedOnly ? '&failed_only=1' : ''
|
28
|
+
}`,
|
29
|
+
JOB_API_KEY
|
30
|
+
);
|
31
|
+
const title = __('Run job');
|
32
|
+
const breadcrumbOptions = {
|
33
|
+
breadcrumbItems: [
|
34
|
+
{ caption: __('Jobs'), url: `/jobs` },
|
35
|
+
{ caption: title },
|
36
|
+
],
|
37
|
+
};
|
38
|
+
|
39
|
+
const jobOrganization = response.job_organization;
|
40
|
+
const jobLocation = response.job_location;
|
41
|
+
const currentOrganization = useForemanOrganization();
|
42
|
+
const currentLocation = useForemanLocation();
|
43
|
+
|
44
|
+
return (
|
45
|
+
<PageLayout
|
46
|
+
header={title}
|
47
|
+
breadcrumbOptions={breadcrumbOptions}
|
48
|
+
searchable={false}
|
49
|
+
>
|
50
|
+
<React.Fragment>
|
51
|
+
<Title headingLevel="h2" size="2xl">
|
52
|
+
{title}
|
53
|
+
</Title>
|
54
|
+
{!status || status === STATUS.PENDING ? (
|
55
|
+
<div style={{ height: '400px' }}>
|
56
|
+
<Skeleton
|
57
|
+
height="100%"
|
58
|
+
screenreaderText="Loading large rectangle contents"
|
59
|
+
/>
|
60
|
+
</div>
|
61
|
+
) : (
|
62
|
+
<React.Fragment>
|
63
|
+
{jobOrganization?.id !== currentOrganization?.id && (
|
64
|
+
<Alert
|
65
|
+
className="job-wizard-alert"
|
66
|
+
variant="warning"
|
67
|
+
title={sprintf(
|
68
|
+
__(
|
69
|
+
"Current organization %s is different from job's organization %s. This job may run on different hosts than before.",
|
70
|
+
currentOrganization,
|
71
|
+
jobOrganization
|
72
|
+
)
|
73
|
+
)}
|
74
|
+
/>
|
75
|
+
)}
|
76
|
+
{jobLocation?.id !== currentLocation?.id && (
|
77
|
+
<Alert
|
78
|
+
className="job-wizard-alert"
|
79
|
+
variant="warning"
|
80
|
+
title={sprintf(
|
81
|
+
__(
|
82
|
+
"Current location %s is different from job's location %s. This job may run on different hosts than before.",
|
83
|
+
currentLocation,
|
84
|
+
jobLocation
|
85
|
+
)
|
86
|
+
)}
|
87
|
+
/>
|
88
|
+
)}
|
89
|
+
<Divider component="div" />
|
90
|
+
<JobWizard
|
91
|
+
rerunData={{ ...response?.job, inputs: response?.inputs } || null}
|
92
|
+
/>
|
93
|
+
</React.Fragment>
|
94
|
+
)}
|
95
|
+
</React.Fragment>
|
96
|
+
</PageLayout>
|
97
|
+
);
|
98
|
+
};
|
99
|
+
JobWizardPageRerun.propTypes = {
|
100
|
+
match: PropTypes.shape({
|
101
|
+
params: PropTypes.shape({
|
102
|
+
id: PropTypes.string.isRequired,
|
103
|
+
}),
|
104
|
+
}).isRequired,
|
105
|
+
location: PropTypes.shape({
|
106
|
+
search: PropTypes.string,
|
107
|
+
}),
|
108
|
+
};
|
109
|
+
JobWizardPageRerun.defaultProps = {
|
110
|
+
location: { search: '' },
|
111
|
+
};
|
112
|
+
export default JobWizardPageRerun;
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Provider } from 'react-redux';
|
3
|
+
import { render, fireEvent, screen, act } from '@testing-library/react';
|
4
|
+
import { MockedProvider } from '@apollo/client/testing';
|
5
|
+
|
6
|
+
import * as APIHooks from 'foremanReact/common/hooks/API/APIHooks';
|
7
|
+
import * as api from 'foremanReact/redux/API';
|
8
|
+
import JobWizardPageRerun from '../JobWizardPageRerun';
|
9
|
+
import * as selectors from '../JobWizardSelectors';
|
10
|
+
import { testSetup, mockApi, gqlMock, jobInvocation } from './fixtures';
|
11
|
+
|
12
|
+
const store = testSetup(selectors, api);
|
13
|
+
mockApi(api);
|
14
|
+
jest.spyOn(APIHooks, 'useAPI');
|
15
|
+
APIHooks.useAPI.mockImplementation((action, url) => {
|
16
|
+
if (url === '/ui_job_wizard/job_invocation?id=57') {
|
17
|
+
return { response: jobInvocation, status: 'RESOLVED' };
|
18
|
+
}
|
19
|
+
return {};
|
20
|
+
});
|
21
|
+
|
22
|
+
describe('Job wizard fill', () => {
|
23
|
+
it('fill defaults into fields', async () => {
|
24
|
+
render(
|
25
|
+
<MockedProvider mocks={gqlMock} addTypename={false}>
|
26
|
+
<Provider store={store}>
|
27
|
+
<JobWizardPageRerun
|
28
|
+
match={{
|
29
|
+
params: { id: '57' },
|
30
|
+
}}
|
31
|
+
/>
|
32
|
+
</Provider>
|
33
|
+
</MockedProvider>
|
34
|
+
);
|
35
|
+
await act(async () => {
|
36
|
+
fireEvent.click(screen.getByText('Target hosts and inputs'));
|
37
|
+
});
|
38
|
+
await screen.findByLabelText('plain hidden', {
|
39
|
+
selector: 'textarea',
|
40
|
+
});
|
41
|
+
|
42
|
+
expect(
|
43
|
+
screen.getByLabelText('plain hidden', {
|
44
|
+
selector: 'textarea',
|
45
|
+
}).value
|
46
|
+
).toBe('test command');
|
47
|
+
|
48
|
+
await act(async () => {
|
49
|
+
fireEvent.click(screen.getByText('Advanced fields'));
|
50
|
+
});
|
51
|
+
|
52
|
+
expect(
|
53
|
+
screen.getByLabelText('ssh user', {
|
54
|
+
selector: 'input',
|
55
|
+
}).value
|
56
|
+
).toBe('ssh user');
|
57
|
+
expect(
|
58
|
+
screen.getByLabelText('effective user', {
|
59
|
+
selector: 'input',
|
60
|
+
}).value
|
61
|
+
).toBe('Effective user');
|
62
|
+
expect(
|
63
|
+
screen.getByLabelText('timeout to kill', {
|
64
|
+
selector: 'input',
|
65
|
+
}).value
|
66
|
+
).toBe('1');
|
67
|
+
|
68
|
+
expect(
|
69
|
+
screen.getByLabelText('Concurrency level', {
|
70
|
+
selector: 'input',
|
71
|
+
}).value
|
72
|
+
).toBe('6');
|
73
|
+
expect(
|
74
|
+
screen.getByLabelText('Time span', {
|
75
|
+
selector: 'input',
|
76
|
+
}).value
|
77
|
+
).toBe('4');
|
78
|
+
});
|
79
|
+
});
|
@@ -106,6 +106,7 @@ export const testSetup = (selectors, api) => {
|
|
106
106
|
jest.spyOn(selectors, 'selectJobCategories');
|
107
107
|
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
108
108
|
jest.spyOn(selectors, 'selectWithKatello');
|
109
|
+
jest.spyOn(selectors, 'selectEffectiveUser');
|
109
110
|
|
110
111
|
jest.spyOn(selectors, 'selectTemplateInputs');
|
111
112
|
jest.spyOn(selectors, 'selectAdvancedTemplateInputs');
|
@@ -122,6 +123,10 @@ export const testSetup = (selectors, api) => {
|
|
122
123
|
{ ...jobTemplate, id: 2, name: 'template2' },
|
123
124
|
]);
|
124
125
|
selectors.selectJobTemplate.mockImplementation(() => jobTemplateResponse);
|
126
|
+
|
127
|
+
selectors.selectEffectiveUser.mockImplementation(
|
128
|
+
() => jobTemplateResponse.effective_user
|
129
|
+
);
|
125
130
|
const mockStore = configureMockStore([]);
|
126
131
|
const store = mockStore({
|
127
132
|
ForemanTasksTask: {
|
@@ -224,3 +229,71 @@ export const gqlMock = [
|
|
224
229
|
},
|
225
230
|
},
|
226
231
|
];
|
232
|
+
|
233
|
+
export const jobInvocation = {
|
234
|
+
job: {
|
235
|
+
job_category: 'Ansible Commands',
|
236
|
+
targeting: {
|
237
|
+
user_id: 4,
|
238
|
+
search_query: 'name ~ *',
|
239
|
+
bookmark_id: null,
|
240
|
+
targeting_type: 'static_query',
|
241
|
+
randomized_ordering: true,
|
242
|
+
},
|
243
|
+
triggering: {
|
244
|
+
mode: 'immediate',
|
245
|
+
start_at: null,
|
246
|
+
start_before: null,
|
247
|
+
},
|
248
|
+
ssh_user: 'ssh user',
|
249
|
+
description_format: null,
|
250
|
+
concurrency_control: {
|
251
|
+
level: 6,
|
252
|
+
time_span: 4,
|
253
|
+
},
|
254
|
+
execution_timeout_interval: 1,
|
255
|
+
remote_execution_feature_id: null,
|
256
|
+
template_invocations: [
|
257
|
+
{
|
258
|
+
template_id: 263,
|
259
|
+
effective_user: 'Effective user',
|
260
|
+
input_values: [
|
261
|
+
{
|
262
|
+
template_input_id: 162,
|
263
|
+
value: 'test command',
|
264
|
+
},
|
265
|
+
],
|
266
|
+
},
|
267
|
+
],
|
268
|
+
reruns: 57,
|
269
|
+
},
|
270
|
+
job_organization: {
|
271
|
+
id: 5,
|
272
|
+
name: 'ana-praley',
|
273
|
+
created_at: '2021-08-26T13:47:35.655+02:00',
|
274
|
+
updated_at: '2021-08-26T13:48:21.435+02:00',
|
275
|
+
ignore_types: [],
|
276
|
+
description: null,
|
277
|
+
label: 'ana-praley',
|
278
|
+
ancestry: null,
|
279
|
+
title: 'ana-praley',
|
280
|
+
manifest_refreshed_at: null,
|
281
|
+
created_in_katello: true,
|
282
|
+
},
|
283
|
+
job_location: {
|
284
|
+
id: 2,
|
285
|
+
name: 'Default Location',
|
286
|
+
created_at: '2021-08-24T15:32:18.830+02:00',
|
287
|
+
updated_at: '2021-08-24T15:32:18.830+02:00',
|
288
|
+
ignore_types: ['ProvisioningTemplate', 'Hostgroup'],
|
289
|
+
description: null,
|
290
|
+
label: null,
|
291
|
+
ancestry: null,
|
292
|
+
title: 'Default Location',
|
293
|
+
manifest_refreshed_at: null,
|
294
|
+
created_in_katello: false,
|
295
|
+
},
|
296
|
+
inputs: {
|
297
|
+
'inputs[plain hidden]': 'test command',
|
298
|
+
},
|
299
|
+
};
|
@@ -45,7 +45,7 @@ describe('Job wizard fill', () => {
|
|
45
45
|
</Provider>
|
46
46
|
);
|
47
47
|
expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
|
48
|
-
|
48
|
+
5
|
49
49
|
);
|
50
50
|
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
51
51
|
expect(store.getActions()).toMatchSnapshot('initial');
|
@@ -79,8 +79,12 @@ describe('Job wizard fill', () => {
|
|
79
79
|
</Provider>
|
80
80
|
</MockedProvider>
|
81
81
|
);
|
82
|
-
const
|
83
|
-
|
82
|
+
const steps = [
|
83
|
+
WIZARD_TITLES.hostsAndInputs,
|
84
|
+
WIZARD_TITLES.categoryAndTemplate,
|
85
|
+
WIZARD_TITLES.advanced,
|
86
|
+
WIZARD_TITLES.review,
|
87
|
+
];
|
84
88
|
// eslint-disable-next-line no-unused-vars
|
85
89
|
for await (const step of steps) {
|
86
90
|
const stepSelector = screen.getByText(step);
|
@@ -92,5 +96,15 @@ describe('Job wizard fill', () => {
|
|
92
96
|
const stepTitles = screen.getAllByText(step);
|
93
97
|
expect(stepTitles).toHaveLength(3);
|
94
98
|
}
|
99
|
+
const step = WIZARD_TITLES.typeOfExecution;
|
100
|
+
const stepTitle = screen.getAllByText(step);
|
101
|
+
expect(stepTitle).toHaveLength(1);
|
102
|
+
expect(screen.queryAllByText('Select the type of execution')).toHaveLength(
|
103
|
+
0
|
104
|
+
);
|
105
|
+
await act(async () => {
|
106
|
+
await fireEvent.click(stepTitle[0]);
|
107
|
+
});
|
108
|
+
expect(screen.getAllByText('Select the type of execution')).toHaveLength(1);
|
95
109
|
});
|
96
110
|
});
|
@@ -11,6 +11,7 @@ export const useAutoFill = ({
|
|
11
11
|
setHostsSearchQuery,
|
12
12
|
setJobTemplateID,
|
13
13
|
setTemplateValues,
|
14
|
+
setAdvancedValues,
|
14
15
|
}) => {
|
15
16
|
const dispatch = useDispatch();
|
16
17
|
|
@@ -49,15 +50,21 @@ export const useAutoFill = ({
|
|
49
50
|
},
|
50
51
|
})
|
51
52
|
);
|
53
|
+
}
|
54
|
+
if (rest) {
|
52
55
|
Object.keys(rest).forEach(key => {
|
53
56
|
const re = /inputs\[(?<input>.*)\]/g;
|
54
57
|
const input = re.exec(key)?.groups?.input;
|
55
58
|
if (input) {
|
56
59
|
setTemplateValues(prev => ({ ...prev, [input]: rest[key] }));
|
60
|
+
setAdvancedValues(prev => ({
|
61
|
+
...prev,
|
62
|
+
templateValues: { ...prev.templateValues, [input]: rest[key] },
|
63
|
+
}));
|
57
64
|
}
|
58
65
|
});
|
59
66
|
}
|
60
67
|
}
|
61
68
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
62
|
-
}, []);
|
69
|
+
}, [fills]);
|
63
70
|
};
|
@@ -17,14 +17,14 @@ import {
|
|
17
17
|
} from '../../../__tests__/fixtures';
|
18
18
|
import { WIZARD_TITLES } from '../../../JobWizardConstants';
|
19
19
|
|
20
|
+
const lodash = require('lodash');
|
21
|
+
|
22
|
+
lodash.debounce = fn => fn;
|
20
23
|
const store = testSetup(selectors, api);
|
21
24
|
mockApi(api);
|
22
25
|
|
23
|
-
jest.
|
26
|
+
jest.useFakeTimers();
|
24
27
|
|
25
|
-
selectors.selectEffectiveUser.mockImplementation(
|
26
|
-
() => jobTemplateResponse.effective_user
|
27
|
-
);
|
28
28
|
describe('AdvancedFields', () => {
|
29
29
|
it('should save data between steps for advanced fields', async () => {
|
30
30
|
const wrapper = mount(
|
@@ -49,6 +49,10 @@ describe('AdvancedFields', () => {
|
|
49
49
|
.find('.pf-c-wizard__nav-link')
|
50
50
|
.at(2)
|
51
51
|
.simulate('click'); // Advanced step
|
52
|
+
|
53
|
+
await act(async () => {
|
54
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
55
|
+
});
|
52
56
|
const effectiveUserInput = () => wrapper.find('input#effective-user');
|
53
57
|
const advancedTemplateInput = () =>
|
54
58
|
wrapper.find('.pf-c-form__group-control textarea');
|
@@ -78,6 +82,9 @@ describe('AdvancedFields', () => {
|
|
78
82
|
.at(2)
|
79
83
|
.simulate('click'); // Advanced step
|
80
84
|
|
85
|
+
await act(async () => {
|
86
|
+
jest.runOnlyPendingTimers(); // to handle pf4 date picker popover
|
87
|
+
});
|
81
88
|
expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
|
82
89
|
expect(advancedTemplateInput().prop('value')).toEqual(
|
83
90
|
advancedTemplateInputValue
|
@@ -93,10 +100,13 @@ describe('AdvancedFields', () => {
|
|
93
100
|
);
|
94
101
|
await act(async () => {
|
95
102
|
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
103
|
+
jest.runOnlyPendingTimers(); // to handle pf4 date picker popover
|
96
104
|
});
|
105
|
+
|
97
106
|
const searchValue = 'search test';
|
98
107
|
const textValue = 'I am a text';
|
99
|
-
const dateValue = '
|
108
|
+
const dateValue = '2022/06/24';
|
109
|
+
const timeValue = '12:34:56';
|
100
110
|
const textField = screen.getByLabelText('adv plain hidden', {
|
101
111
|
selector: 'textarea',
|
102
112
|
});
|
@@ -105,9 +115,8 @@ describe('AdvancedFields', () => {
|
|
105
115
|
'adv resource select toggle'
|
106
116
|
);
|
107
117
|
const searchField = screen.getByPlaceholderText('Filter...');
|
108
|
-
const dateField = screen.getByLabelText('adv date'
|
109
|
-
|
110
|
-
});
|
118
|
+
const dateField = screen.getByLabelText('adv date datepicker');
|
119
|
+
const timeField = screen.getByLabelText('adv date timepicker');
|
111
120
|
|
112
121
|
fireEvent.click(selectField);
|
113
122
|
await act(async () => {
|
@@ -115,18 +124,20 @@ describe('AdvancedFields', () => {
|
|
115
124
|
fireEvent.click(screen.getAllByText(WIZARD_TITLES.advanced)[0]); // to remove focus
|
116
125
|
|
117
126
|
fireEvent.click(resourceSelectField);
|
118
|
-
|
119
|
-
|
120
|
-
await fireEvent.change(textField, {
|
127
|
+
fireEvent.click(screen.getByText('resource2'));
|
128
|
+
fireEvent.change(textField, {
|
121
129
|
target: { value: textValue },
|
122
130
|
});
|
123
|
-
|
124
|
-
await fireEvent.change(searchField, {
|
131
|
+
fireEvent.change(searchField, {
|
125
132
|
target: { value: searchValue },
|
126
133
|
});
|
127
134
|
await fireEvent.change(dateField, {
|
128
135
|
target: { value: dateValue },
|
129
136
|
});
|
137
|
+
fireEvent.change(timeField, {
|
138
|
+
target: { value: timeValue },
|
139
|
+
});
|
140
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
130
141
|
});
|
131
142
|
expect(
|
132
143
|
screen.getByLabelText('adv plain hidden', {
|
@@ -134,7 +145,8 @@ describe('AdvancedFields', () => {
|
|
134
145
|
}).value
|
135
146
|
).toBe(textValue);
|
136
147
|
expect(searchField.value).toBe(searchValue);
|
137
|
-
expect(
|
148
|
+
expect(screen.getByLabelText('adv date datepicker').value).toBe(dateValue);
|
149
|
+
expect(timeField.value).toBe(timeValue);
|
138
150
|
await act(async () => {
|
139
151
|
fireEvent.click(screen.getByText(WIZARD_TITLES.categoryAndTemplate));
|
140
152
|
});
|
@@ -143,11 +155,13 @@ describe('AdvancedFields', () => {
|
|
143
155
|
);
|
144
156
|
|
145
157
|
await act(async () => {
|
146
|
-
fireEvent.click(screen.getByText(
|
158
|
+
await fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
159
|
+
jest.runOnlyPendingTimers();
|
147
160
|
});
|
148
161
|
expect(textField.value).toBe(textValue);
|
149
162
|
expect(searchField.value).toBe(searchValue);
|
150
163
|
expect(dateField.value).toBe(dateValue);
|
164
|
+
expect(timeField.value).toBe(timeValue);
|
151
165
|
expect(screen.queryAllByText('option 1')).toHaveLength(0);
|
152
166
|
expect(screen.queryAllByText('option 2')).toHaveLength(1);
|
153
167
|
expect(screen.queryAllByDisplayValue('resource1')).toHaveLength(0);
|
@@ -162,7 +176,8 @@ describe('AdvancedFields', () => {
|
|
162
176
|
</MockedProvider>
|
163
177
|
);
|
164
178
|
await act(async () => {
|
165
|
-
fireEvent.click(screen.getByText(
|
179
|
+
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
180
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
166
181
|
});
|
167
182
|
|
168
183
|
expect(
|
@@ -197,6 +212,7 @@ describe('AdvancedFields', () => {
|
|
197
212
|
);
|
198
213
|
await act(async () => {
|
199
214
|
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
215
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
200
216
|
});
|
201
217
|
|
202
218
|
const textField = screen.getByLabelText('adv plain hidden', {
|
@@ -264,6 +280,7 @@ describe('AdvancedFields', () => {
|
|
264
280
|
);
|
265
281
|
await act(async () => {
|
266
282
|
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
283
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
267
284
|
});
|
268
285
|
expect(
|
269
286
|
screen.getByLabelText('description preview', {
|
@@ -332,6 +349,7 @@ describe('AdvancedFields', () => {
|
|
332
349
|
);
|
333
350
|
await act(async () => {
|
334
351
|
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
352
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
335
353
|
});
|
336
354
|
expect(
|
337
355
|
screen.getByLabelText('description preview', {
|
@@ -351,6 +369,7 @@ describe('AdvancedFields', () => {
|
|
351
369
|
);
|
352
370
|
await act(async () => {
|
353
371
|
fireEvent.click(screen.getByText(WIZARD_TITLES.advanced));
|
372
|
+
jest.runAllTimers(); // to handle pf4 date picker popover
|
354
373
|
});
|
355
374
|
const resourceSelectField = screen.getByLabelText(
|
356
375
|
'adv resource select typeahead input'
|
@@ -361,7 +380,7 @@ describe('AdvancedFields', () => {
|
|
361
380
|
target: { value: 'some search' },
|
362
381
|
});
|
363
382
|
|
364
|
-
|
383
|
+
jest.runAllTimers();
|
365
384
|
});
|
366
385
|
expect(newStore.getActions()).toMatchSnapshot('resource search');
|
367
386
|
});
|
@@ -25,7 +25,7 @@ const ConnectedCategoryAndTemplate = ({
|
|
25
25
|
setJobTemplate,
|
26
26
|
category,
|
27
27
|
setCategory,
|
28
|
-
|
28
|
+
isCategoryPreselected,
|
29
29
|
}) => {
|
30
30
|
const dispatch = useDispatch();
|
31
31
|
|
@@ -44,7 +44,7 @@ const ConnectedCategoryAndTemplate = ({
|
|
44
44
|
default_template: defaultTemplate,
|
45
45
|
},
|
46
46
|
}) => {
|
47
|
-
if (!
|
47
|
+
if (!isCategoryPreselected) {
|
48
48
|
setCategory(defaultCategory || jobCategories[0] || '');
|
49
49
|
if (defaultTemplate) setJobTemplate(defaultTemplate);
|
50
50
|
}
|
@@ -106,7 +106,7 @@ ConnectedCategoryAndTemplate.propTypes = {
|
|
106
106
|
setJobTemplate: PropTypes.func.isRequired,
|
107
107
|
category: PropTypes.string.isRequired,
|
108
108
|
setCategory: PropTypes.func.isRequired,
|
109
|
-
|
109
|
+
isCategoryPreselected: PropTypes.bool.isRequired,
|
110
110
|
};
|
111
111
|
ConnectedCategoryAndTemplate.defaultProps = { jobTemplate: null };
|
112
112
|
|
@@ -108,9 +108,7 @@ const ReviewDetails = ({
|
|
108
108
|
const detailsSecondHalf = [
|
109
109
|
{
|
110
110
|
label: __('Schedule type'),
|
111
|
-
value: scheduleValue.
|
112
|
-
? __('Schedule for future execution')
|
113
|
-
: __('Execute now'),
|
111
|
+
value: scheduleValue.scheduleType,
|
114
112
|
},
|
115
113
|
{
|
116
114
|
label: __('Recurrence'),
|
@@ -4,7 +4,7 @@ import { TextInput, FormGroup } from '@patternfly/react-core';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
import { helpLabel } from '../form/FormHelpers';
|
6
6
|
|
7
|
-
export const PurposeField = ({
|
7
|
+
export const PurposeField = ({ purpose, setPurpose }) => (
|
8
8
|
<FormGroup
|
9
9
|
label={__('Purpose')}
|
10
10
|
labelIcon={helpLabel(
|
@@ -14,7 +14,6 @@ export const PurposeField = ({ isDisabled, purpose, setPurpose }) => (
|
|
14
14
|
)}
|
15
15
|
>
|
16
16
|
<TextInput
|
17
|
-
isDisabled={isDisabled}
|
18
17
|
aria-label="purpose"
|
19
18
|
type="text"
|
20
19
|
value={purpose}
|
@@ -25,7 +24,6 @@ export const PurposeField = ({ isDisabled, purpose, setPurpose }) => (
|
|
25
24
|
</FormGroup>
|
26
25
|
);
|
27
26
|
PurposeField.propTypes = {
|
28
|
-
isDisabled: PropTypes.bool.isRequired,
|
29
27
|
purpose: PropTypes.string.isRequired,
|
30
28
|
setPurpose: PropTypes.func.isRequired,
|
31
29
|
};
|
@@ -1,48 +1,41 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
1
|
import { FormGroup, Radio } from '@patternfly/react-core';
|
4
2
|
import { translate as __ } from 'foremanReact/common/I18n';
|
3
|
+
import PropTypes from 'prop-types';
|
4
|
+
import React from 'react';
|
5
5
|
import { helpLabel } from '../form/FormHelpers';
|
6
6
|
|
7
7
|
export const QueryType = ({ isTypeStatic, setIsTypeStatic }) => (
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
isChecked={!isTypeStatic}
|
40
|
-
name="query-type"
|
41
|
-
onChange={() => setIsTypeStatic(false)}
|
42
|
-
id="query-type-dynamic"
|
43
|
-
label={__('Dynamic query')}
|
44
|
-
/>
|
45
|
-
</FormGroup>
|
8
|
+
<>
|
9
|
+
<FormGroup
|
10
|
+
label={__('Query type')}
|
11
|
+
fieldId="query-type-static"
|
12
|
+
labelIcon={helpLabel(
|
13
|
+
__('Type has impact on when is the query evaluated to hosts.'),
|
14
|
+
'query-type'
|
15
|
+
)}
|
16
|
+
>
|
17
|
+
<Radio
|
18
|
+
isChecked={isTypeStatic}
|
19
|
+
name="query-type"
|
20
|
+
onChange={() => setIsTypeStatic(true)}
|
21
|
+
id="query-type-static"
|
22
|
+
label={__('Static query')}
|
23
|
+
body={__('evaluates just after you submit this form')}
|
24
|
+
/>
|
25
|
+
</FormGroup>
|
26
|
+
<FormGroup fieldId="query-type-dynamic">
|
27
|
+
<Radio
|
28
|
+
isChecked={!isTypeStatic}
|
29
|
+
name="query-type"
|
30
|
+
onChange={() => setIsTypeStatic(false)}
|
31
|
+
id="query-type-dynamic"
|
32
|
+
label={__('Dynamic query')}
|
33
|
+
body={__(
|
34
|
+
"evaluates just before the execution is started, so if it's planed in future, targeted hosts set may change before it"
|
35
|
+
)}
|
36
|
+
/>
|
37
|
+
</FormGroup>
|
38
|
+
</>
|
46
39
|
);
|
47
40
|
|
48
41
|
QueryType.propTypes = {
|