foreman_remote_execution 4.5.6 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/job_invocations_controller.rb +1 -1
- data/app/controllers/ui_job_wizard_controller.rb +0 -7
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +1 -5
- data/app/helpers/remote_execution_helper.rb +3 -9
- data/app/lib/actions/remote_execution/run_host_job.rb +1 -5
- data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +0 -2
- data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +70 -0
- data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +0 -6
- data/app/models/host_status/execution_status.rb +3 -3
- data/app/models/job_invocation.rb +6 -9
- data/app/models/job_invocation_composer.rb +4 -4
- data/app/models/remote_execution_feature.rb +1 -5
- data/app/models/remote_execution_provider.rb +2 -3
- data/app/models/setting/remote_execution.rb +2 -2
- data/app/models/targeting.rb +1 -5
- data/app/views/job_invocations/index.html.erb +1 -1
- data/app/views/templates/ssh/module_action.erb +0 -1
- data/app/views/templates/ssh/power_action.erb +0 -2
- data/app/views/templates/ssh/puppet_run_once.erb +0 -1
- data/foreman_remote_execution.gemspec +0 -1
- data/lib/foreman_remote_execution/engine.rb +2 -0
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/test/models/orchestration/ssh_test.rb +56 -0
- data/test/unit/job_invocation_composer_test.rb +1 -14
- data/test/unit/job_invocation_test.rb +1 -1
- data/test/unit/remote_execution_provider_test.rb +0 -12
- data/webpack/JobWizard/JobWizard.js +16 -59
- data/webpack/JobWizard/JobWizard.scss +0 -58
- data/webpack/JobWizard/JobWizardConstants.js +0 -18
- data/webpack/JobWizard/JobWizardSelectors.js +0 -9
- data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
- data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
- data/webpack/JobWizard/__tests__/fixtures.js +2 -112
- data/webpack/JobWizard/__tests__/integration.test.js +92 -16
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +9 -37
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +55 -116
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +16 -150
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +2 -4
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +51 -123
- data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +0 -1
- data/webpack/JobWizard/steps/form/SelectField.js +2 -14
- data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
- data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
- data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
- data/webpack/__mocks__/foremanReact/components/SearchBar.js +1 -18
- data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +0 -1
- metadata +13 -35
- data/app/models/host_proxy_invocation.rb +0 -4
- data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +0 -12
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +0 -67
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +0 -25
- data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +0 -23
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/SelectedChips.test.js +0 -37
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +0 -50
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +0 -66
- data/webpack/JobWizard/steps/Schedule/QueryType.js +0 -48
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +0 -61
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +0 -25
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +0 -51
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +0 -22
- data/webpack/JobWizard/steps/Schedule/index.js +0 -44
- data/webpack/JobWizard/steps/form/Formatter.js +0 -150
- data/webpack/JobWizard/steps/form/NumberInput.js +0 -35
- data/webpack/JobWizard/steps/form/WizardTitle.js +0 -14
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +0 -76
@@ -1,64 +1,28 @@
|
|
1
|
-
/* eslint-disable camelcase */
|
2
1
|
import React, { useState, useEffect, useCallback } from 'react';
|
3
2
|
import { useDispatch, useSelector } from 'react-redux';
|
4
3
|
import { Wizard } from '@patternfly/react-core';
|
5
4
|
import { get } from 'foremanReact/redux/API';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
6
|
import history from 'foremanReact/history';
|
7
7
|
import CategoryAndTemplate from './steps/CategoryAndTemplate/';
|
8
8
|
import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
|
9
|
-
import { JOB_TEMPLATE
|
9
|
+
import { JOB_TEMPLATE } from './JobWizardConstants';
|
10
10
|
import { selectTemplateError } from './JobWizardSelectors';
|
11
|
-
import Schedule from './steps/Schedule/';
|
12
|
-
import HostsAndInputs from './steps/HostsAndInputs/';
|
13
11
|
import './JobWizard.scss';
|
14
12
|
|
15
13
|
export const JobWizard = () => {
|
16
14
|
const [jobTemplateID, setJobTemplateID] = useState(null);
|
17
15
|
const [category, setCategory] = useState('');
|
18
16
|
const [advancedValues, setAdvancedValues] = useState({});
|
19
|
-
const [templateValues, setTemplateValues] = useState({}); // TODO use templateValues in advanced fields - description https://github.com/theforeman/foreman_remote_execution/pull/605
|
20
|
-
const [selectedHosts, setSelectedHosts] = useState(['host1', 'host2']);
|
21
17
|
const dispatch = useDispatch();
|
22
18
|
|
23
|
-
const setDefaults = useCallback(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
},
|
31
|
-
}) => {
|
32
|
-
const advancedTemplateValues = {};
|
33
|
-
const defaultTemplateValues = {};
|
34
|
-
const inputs = template_inputs;
|
35
|
-
const advancedInputs = advanced_template_inputs;
|
36
|
-
if (inputs) {
|
37
|
-
setTemplateValues(() => {
|
38
|
-
inputs.forEach(input => {
|
39
|
-
defaultTemplateValues[input.name] = input?.default || '';
|
40
|
-
});
|
41
|
-
return defaultTemplateValues;
|
42
|
-
});
|
43
|
-
}
|
44
|
-
setAdvancedValues(currentAdvancedValues => {
|
45
|
-
if (advancedInputs) {
|
46
|
-
advancedInputs.forEach(input => {
|
47
|
-
advancedTemplateValues[input.name] = input?.default || '';
|
48
|
-
});
|
49
|
-
}
|
50
|
-
return {
|
51
|
-
...currentAdvancedValues,
|
52
|
-
effectiveUserValue: effective_user?.value || '',
|
53
|
-
timeoutToKill: execution_timeout_interval || '',
|
54
|
-
templateValues: advancedTemplateValues,
|
55
|
-
description: description_format || '',
|
56
|
-
isRandomizedOrdering: false,
|
57
|
-
};
|
58
|
-
});
|
59
|
-
},
|
60
|
-
[]
|
61
|
-
);
|
19
|
+
const setDefaults = useCallback(response => {
|
20
|
+
const responseJob = response.data;
|
21
|
+
setAdvancedValues({
|
22
|
+
effectiveUserValue: responseJob.effective_user?.value || '',
|
23
|
+
timeoutToKill: responseJob.job_template.execution_timeout_interval || '',
|
24
|
+
});
|
25
|
+
}, []);
|
62
26
|
useEffect(() => {
|
63
27
|
if (jobTemplateID) {
|
64
28
|
dispatch(
|
@@ -75,7 +39,7 @@ export const JobWizard = () => {
|
|
75
39
|
const isTemplate = !templateError && !!jobTemplateID;
|
76
40
|
const steps = [
|
77
41
|
{
|
78
|
-
name:
|
42
|
+
name: __('Category and Template'),
|
79
43
|
component: (
|
80
44
|
<CategoryAndTemplate
|
81
45
|
jobTemplate={jobTemplateID}
|
@@ -86,19 +50,12 @@ export const JobWizard = () => {
|
|
86
50
|
),
|
87
51
|
},
|
88
52
|
{
|
89
|
-
name:
|
90
|
-
component:
|
91
|
-
<HostsAndInputs
|
92
|
-
templateValues={templateValues}
|
93
|
-
setTemplateValues={setTemplateValues}
|
94
|
-
selectedHosts={selectedHosts}
|
95
|
-
setSelectedHosts={setSelectedHosts}
|
96
|
-
/>
|
97
|
-
),
|
53
|
+
name: __('Target Hosts'),
|
54
|
+
component: <p>Target Hosts</p>,
|
98
55
|
canJumpTo: isTemplate,
|
99
56
|
},
|
100
57
|
{
|
101
|
-
name:
|
58
|
+
name: __('Advanced Fields'),
|
102
59
|
component: (
|
103
60
|
<AdvancedFields
|
104
61
|
advancedValues={advancedValues}
|
@@ -114,12 +71,12 @@ export const JobWizard = () => {
|
|
114
71
|
canJumpTo: isTemplate,
|
115
72
|
},
|
116
73
|
{
|
117
|
-
name:
|
118
|
-
component: <Schedule
|
74
|
+
name: __('Schedule'),
|
75
|
+
component: <p>Schedule</p>,
|
119
76
|
canJumpTo: isTemplate,
|
120
77
|
},
|
121
78
|
{
|
122
|
-
name:
|
79
|
+
name: __('Review Details'),
|
123
80
|
component: <p>Review Details</p>,
|
124
81
|
nextButtonText: 'Run',
|
125
82
|
canJumpTo: isTemplate,
|
@@ -1,10 +1,5 @@
|
|
1
1
|
.job-wizard {
|
2
|
-
.wizard-title {
|
3
|
-
margin-bottom: 25px;
|
4
|
-
}
|
5
|
-
|
6
2
|
.pf-c-wizard__main {
|
7
|
-
overflow: visible;
|
8
3
|
z-index: calc(
|
9
4
|
var(--pf-c-wizard__footer--ZIndex) + 1
|
10
5
|
); // So the select box can be shown above the wizard footer
|
@@ -15,58 +10,5 @@
|
|
15
10
|
.advanced-fields-title {
|
16
11
|
margin-bottom: 10px;
|
17
12
|
}
|
18
|
-
#advanced-fields-job-template {
|
19
|
-
.foreman-search-field {
|
20
|
-
.rbt-input-hint input{
|
21
|
-
display: none;
|
22
|
-
}
|
23
|
-
// Giving pf3 search bar a pf4 look
|
24
|
-
.search-bar {
|
25
|
-
display: block;
|
26
|
-
}
|
27
|
-
.input-group-btn {
|
28
|
-
display: none;
|
29
|
-
}
|
30
|
-
li {
|
31
|
-
font-size: 16px;
|
32
|
-
}
|
33
|
-
input {
|
34
|
-
font-size: 16px;
|
35
|
-
height: 36px;
|
36
|
-
}
|
37
|
-
.foreman-autocomplete .autocomplete-focus-shortcut {
|
38
|
-
top: 8px;
|
39
|
-
font-size: 16px;
|
40
|
-
}
|
41
|
-
.foreman-autocomplete .autocomplete-aux {
|
42
|
-
top: 8px;
|
43
|
-
font-size: 16px;
|
44
|
-
.autocomplete-clear-button {
|
45
|
-
font-size: 16px;
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|
52
|
-
.hosts-chip-group {
|
53
|
-
margin-top: 8px;
|
54
|
-
}
|
55
|
-
input[type='radio'],
|
56
|
-
input[type='checkbox'] {
|
57
|
-
margin: 0;
|
58
|
-
}
|
59
|
-
.schedule-tab {
|
60
|
-
input[type='radio'],
|
61
|
-
input[type='checkbox'] {
|
62
|
-
margin: 0;
|
63
|
-
}
|
64
|
-
.advanced-scheduling-button {
|
65
|
-
text-align: start;
|
66
|
-
}
|
67
|
-
}
|
68
|
-
textarea {
|
69
|
-
min-height: 40px;
|
70
|
-
min-width: 100px;
|
71
13
|
}
|
72
14
|
}
|
@@ -1,24 +1,6 @@
|
|
1
|
-
import { translate as __ } from 'foremanReact/common/I18n';
|
2
1
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
3
2
|
|
4
3
|
export const JOB_TEMPLATES = 'JOB_TEMPLATES';
|
5
4
|
export const JOB_CATEGORIES = 'JOB_CATEGORIES';
|
6
5
|
export const JOB_TEMPLATE = 'JOB_TEMPLATE';
|
7
6
|
export const templatesUrl = foremanUrl('/api/v2/job_templates');
|
8
|
-
|
9
|
-
export const repeatTypes = {
|
10
|
-
noRepeat: __('Does not repeat'),
|
11
|
-
cronline: __('Cronline'),
|
12
|
-
monthly: __('Monthly'),
|
13
|
-
weekly: __('Weekly'),
|
14
|
-
daily: __('Daily'),
|
15
|
-
hourly: __('Hourly'),
|
16
|
-
};
|
17
|
-
|
18
|
-
export const WIZARD_TITLES = {
|
19
|
-
categoryAndTemplate: __('Category and Template'),
|
20
|
-
hostsAndInputs: __('Target hosts and inputs'),
|
21
|
-
advanced: __('Advanced Fields'),
|
22
|
-
schedule: __('Schedule'),
|
23
|
-
review: __('Review Details'),
|
24
|
-
};
|
@@ -36,12 +36,3 @@ export const selectTemplateError = state =>
|
|
36
36
|
|
37
37
|
export const selectJobTemplate = state =>
|
38
38
|
selectAPIResponse(state, JOB_TEMPLATE);
|
39
|
-
|
40
|
-
export const selectEffectiveUser = state =>
|
41
|
-
selectAPIResponse(state, JOB_TEMPLATE).effective_user;
|
42
|
-
|
43
|
-
export const selectAdvancedTemplateInputs = state =>
|
44
|
-
selectAPIResponse(state, JOB_TEMPLATE).advanced_template_inputs || [];
|
45
|
-
|
46
|
-
export const selectTemplateInputs = state =>
|
47
|
-
selectAPIResponse(state, JOB_TEMPLATE).template_inputs || [];
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import * as patternfly from '@patternfly/react-core';
|
3
|
+
import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
|
4
|
+
import JobWizardPage from '../index';
|
5
|
+
|
6
|
+
jest.spyOn(patternfly, 'Wizard');
|
7
|
+
patternfly.Wizard.mockImplementation(props => <div>{props.navAriaLabel}</div>);
|
8
|
+
|
9
|
+
const fixtures = {
|
10
|
+
'renders ': {},
|
11
|
+
};
|
12
|
+
describe('JobWizardPage rendering', () =>
|
13
|
+
testComponentSnapshotsWithFixtures(JobWizardPage, fixtures));
|
@@ -0,0 +1,32 @@
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
2
|
+
|
3
|
+
exports[`JobWizardPage rendering renders 1`] = `
|
4
|
+
<PageLayout
|
5
|
+
breadcrumbOptions={
|
6
|
+
Object {
|
7
|
+
"breadcrumbItems": Array [
|
8
|
+
Object {
|
9
|
+
"caption": "Jobs",
|
10
|
+
"url": "/jobs",
|
11
|
+
},
|
12
|
+
Object {
|
13
|
+
"caption": "Run job",
|
14
|
+
},
|
15
|
+
],
|
16
|
+
}
|
17
|
+
}
|
18
|
+
header="Run job"
|
19
|
+
searchable={false}
|
20
|
+
>
|
21
|
+
<Title
|
22
|
+
headingLevel="h2"
|
23
|
+
size="2xl"
|
24
|
+
>
|
25
|
+
Run job
|
26
|
+
</Title>
|
27
|
+
<Divider
|
28
|
+
component="div"
|
29
|
+
/>
|
30
|
+
<JobWizard />
|
31
|
+
</PageLayout>
|
32
|
+
`;
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
export const jobTemplate = {
|
1
|
+
const jobTemplate = {
|
4
2
|
id: 178,
|
5
|
-
name: '
|
3
|
+
name: 'Run Command - Ansible Default',
|
6
4
|
template:
|
7
5
|
"---\n- hosts: all\n tasks:\n - shell:\n cmd: |\n<%= indent(10) { input('command') } %>\n register: out\n - debug: var=out",
|
8
6
|
snippet: false,
|
@@ -25,112 +23,4 @@ export const jobTemplateResponse = {
|
|
25
23
|
overridable: true,
|
26
24
|
current_user: false,
|
27
25
|
},
|
28
|
-
advanced_template_inputs: [
|
29
|
-
{
|
30
|
-
name: 'adv plain hidden',
|
31
|
-
required: true,
|
32
|
-
input_type: 'user',
|
33
|
-
description: 'some Description',
|
34
|
-
advanced: true,
|
35
|
-
value_type: 'plain',
|
36
|
-
resource_type: 'ansible_roles',
|
37
|
-
default: 'Default val',
|
38
|
-
hidden_value: true,
|
39
|
-
},
|
40
|
-
{
|
41
|
-
name: 'adv plain select',
|
42
|
-
required: false,
|
43
|
-
input_type: 'user',
|
44
|
-
options: 'option 1\r\noption 2\r\noption 3\r\noption 4',
|
45
|
-
advanced: true,
|
46
|
-
value_type: 'plain',
|
47
|
-
resource_type: 'ansible_roles',
|
48
|
-
default: '',
|
49
|
-
hidden_value: false,
|
50
|
-
},
|
51
|
-
{
|
52
|
-
name: 'adv search',
|
53
|
-
required: false,
|
54
|
-
options: '',
|
55
|
-
advanced: true,
|
56
|
-
value_type: 'search',
|
57
|
-
resource_type: 'foreman_tasks/tasks',
|
58
|
-
default: '',
|
59
|
-
hidden_value: false,
|
60
|
-
},
|
61
|
-
{
|
62
|
-
name: 'adv date',
|
63
|
-
required: false,
|
64
|
-
options: '',
|
65
|
-
advanced: true,
|
66
|
-
value_type: 'date',
|
67
|
-
resource_type: 'ansible_roles',
|
68
|
-
default: '',
|
69
|
-
hidden_value: false,
|
70
|
-
},
|
71
|
-
],
|
72
|
-
template_inputs: [
|
73
|
-
{
|
74
|
-
name: 'plain hidden',
|
75
|
-
required: true,
|
76
|
-
input_type: 'user',
|
77
|
-
description: 'some Description',
|
78
|
-
advanced: false,
|
79
|
-
value_type: 'plain',
|
80
|
-
resource_type: 'ansible_roles',
|
81
|
-
default: 'Default val',
|
82
|
-
hidden_value: true,
|
83
|
-
},
|
84
|
-
],
|
85
|
-
};
|
86
|
-
|
87
|
-
export const jobCategories = ['Ansible Commands', 'Puppet', 'Services'];
|
88
|
-
|
89
|
-
export const testSetup = (selectors, api) => {
|
90
|
-
jest.spyOn(api, 'get');
|
91
|
-
jest.spyOn(selectors, 'selectJobTemplate');
|
92
|
-
jest.spyOn(selectors, 'selectJobTemplates');
|
93
|
-
jest.spyOn(selectors, 'selectJobCategories');
|
94
|
-
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
95
|
-
|
96
|
-
jest.spyOn(selectors, 'selectTemplateInputs');
|
97
|
-
jest.spyOn(selectors, 'selectAdvancedTemplateInputs');
|
98
|
-
selectors.selectTemplateInputs.mockImplementation(
|
99
|
-
() => jobTemplateResponse.template_inputs
|
100
|
-
);
|
101
|
-
selectors.selectAdvancedTemplateInputs.mockImplementation(
|
102
|
-
() => jobTemplateResponse.advanced_template_inputs
|
103
|
-
);
|
104
|
-
selectors.selectJobCategories.mockImplementation(() => jobCategories);
|
105
|
-
selectors.selectJobTemplates.mockImplementation(() => [
|
106
|
-
jobTemplate,
|
107
|
-
{ ...jobTemplate, id: 2, name: 'template2' },
|
108
|
-
]);
|
109
|
-
const mockStore = configureMockStore([]);
|
110
|
-
const store = mockStore({});
|
111
|
-
return store;
|
112
|
-
};
|
113
|
-
|
114
|
-
export const mockTemplate = selectors => {
|
115
|
-
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
116
|
-
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
117
|
-
};
|
118
|
-
export const mockApi = api => {
|
119
|
-
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
120
|
-
if (action.key === 'JOB_CATEGORIES') {
|
121
|
-
handleSuccess &&
|
122
|
-
handleSuccess({ data: { job_categories: jobCategories } });
|
123
|
-
} else if (action.key === 'JOB_TEMPLATE') {
|
124
|
-
handleSuccess &&
|
125
|
-
handleSuccess({
|
126
|
-
data: jobTemplateResponse,
|
127
|
-
});
|
128
|
-
} else if (action.key === 'JOB_TEMPLATES') {
|
129
|
-
handleSuccess &&
|
130
|
-
handleSuccess({
|
131
|
-
data: { results: [jobTemplate] },
|
132
|
-
});
|
133
|
-
}
|
134
|
-
return { type: 'get', ...action };
|
135
|
-
});
|
136
26
|
};
|
@@ -1,28 +1,40 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { Provider } from 'react-redux';
|
3
|
+
import configureMockStore from 'redux-mock-store';
|
3
4
|
import { mount } from '@theforeman/test';
|
4
5
|
import { render, fireEvent, screen, act } from '@testing-library/react';
|
5
6
|
import * as api from 'foremanReact/redux/API';
|
6
7
|
import { JobWizard } from '../JobWizard';
|
7
8
|
import * as selectors from '../JobWizardSelectors';
|
8
|
-
import {
|
9
|
-
import {
|
10
|
-
testSetup,
|
11
|
-
mockApi,
|
12
|
-
jobCategories,
|
13
|
-
jobTemplateResponse as jobTemplate,
|
14
|
-
} from './fixtures';
|
9
|
+
import { jobTemplates, jobTemplateResponse as jobTemplate } from './fixtures';
|
15
10
|
|
16
|
-
|
11
|
+
jest.spyOn(api, 'get');
|
12
|
+
jest.spyOn(selectors, 'selectJobTemplate');
|
13
|
+
jest.spyOn(selectors, 'selectJobTemplates');
|
14
|
+
jest.spyOn(selectors, 'selectJobCategories');
|
15
|
+
jest.spyOn(selectors, 'selectJobCategoriesStatus');
|
17
16
|
|
18
|
-
|
17
|
+
const jobCategories = ['Ansible Commands', 'Puppet', 'Services'];
|
19
18
|
|
20
19
|
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
21
20
|
if (action.key === 'JOB_CATEGORIES') {
|
22
21
|
handleSuccess && handleSuccess({ data: { job_categories: jobCategories } });
|
22
|
+
} else if (action.key === 'JOB_TEMPLATE') {
|
23
|
+
handleSuccess &&
|
24
|
+
handleSuccess({
|
25
|
+
data: jobTemplate,
|
26
|
+
});
|
23
27
|
}
|
24
28
|
return { type: 'get', ...action };
|
25
29
|
});
|
30
|
+
|
31
|
+
selectors.selectJobTemplate.mockImplementation(() => null);
|
32
|
+
selectors.selectJobCategories.mockImplementation(() => jobCategories);
|
33
|
+
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
|
34
|
+
selectors.selectJobTemplates.mockImplementation(() => jobTemplates);
|
35
|
+
|
36
|
+
const mockStore = configureMockStore([]);
|
37
|
+
const store = mockStore({});
|
26
38
|
describe('Job wizard fill', () => {
|
27
39
|
it('should select template', async () => {
|
28
40
|
const wrapper = mount(
|
@@ -39,10 +51,7 @@ describe('Job wizard fill', () => {
|
|
39
51
|
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
40
52
|
wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
|
41
53
|
await act(async () => {
|
42
|
-
await wrapper
|
43
|
-
.find('.pf-c-select__menu-item')
|
44
|
-
.first()
|
45
|
-
.simulate('click');
|
54
|
+
await wrapper.find('.pf-c-select__menu-item').simulate('click');
|
46
55
|
await wrapper.update();
|
47
56
|
});
|
48
57
|
expect(store.getActions().slice(-1)).toMatchSnapshot('select template');
|
@@ -51,20 +60,87 @@ describe('Job wizard fill', () => {
|
|
51
60
|
);
|
52
61
|
});
|
53
62
|
|
63
|
+
it('should save data between steps for advanced fields', async () => {
|
64
|
+
const wrapper = mount(
|
65
|
+
<Provider store={store}>
|
66
|
+
<JobWizard advancedValues={{}} setAdvancedValues={jest.fn()} />
|
67
|
+
</Provider>
|
68
|
+
);
|
69
|
+
// setup
|
70
|
+
selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
|
71
|
+
selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
|
72
|
+
wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
|
73
|
+
wrapper.find('.pf-c-select__menu-item').simulate('click');
|
74
|
+
|
75
|
+
// test
|
76
|
+
expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
|
77
|
+
0
|
78
|
+
);
|
79
|
+
wrapper
|
80
|
+
.find('.pf-c-wizard__nav-link')
|
81
|
+
.at(2)
|
82
|
+
.simulate('click'); // Advanced step
|
83
|
+
const effectiveUserInput = () => wrapper.find('input#effective-user');
|
84
|
+
const effectiveUesrValue = 'effective user new value';
|
85
|
+
effectiveUserInput().getDOMNode().value = effectiveUesrValue;
|
86
|
+
await act(async () => {
|
87
|
+
await effectiveUserInput().simulate('change');
|
88
|
+
wrapper.update();
|
89
|
+
});
|
90
|
+
|
91
|
+
expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
|
92
|
+
|
93
|
+
wrapper
|
94
|
+
.find('.pf-c-wizard__nav-link')
|
95
|
+
.at(1)
|
96
|
+
.simulate('click');
|
97
|
+
|
98
|
+
expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-current').text()).toEqual(
|
99
|
+
'Target Hosts'
|
100
|
+
);
|
101
|
+
wrapper
|
102
|
+
.find('.pf-c-wizard__nav-link')
|
103
|
+
.at(2)
|
104
|
+
.simulate('click'); // Advanced step
|
105
|
+
|
106
|
+
expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
|
107
|
+
});
|
108
|
+
|
54
109
|
it('have all steps', async () => {
|
55
110
|
selectors.selectJobCategoriesStatus.mockImplementation(() => null);
|
56
111
|
selectors.selectJobTemplate.mockRestore();
|
57
112
|
selectors.selectJobTemplates.mockRestore();
|
58
113
|
selectors.selectJobCategories.mockRestore();
|
59
|
-
|
114
|
+
api.get.mockImplementation(({ handleSuccess, ...action }) => {
|
115
|
+
if (action.key === 'JOB_CATEGORIES') {
|
116
|
+
handleSuccess &&
|
117
|
+
handleSuccess({ data: { job_categories: jobCategories } });
|
118
|
+
} else if (action.key === 'JOB_TEMPLATE') {
|
119
|
+
handleSuccess &&
|
120
|
+
handleSuccess({
|
121
|
+
data: jobTemplate,
|
122
|
+
});
|
123
|
+
} else if (action.key === 'JOB_TEMPLATES') {
|
124
|
+
handleSuccess &&
|
125
|
+
handleSuccess({
|
126
|
+
data: { results: [jobTemplate.job_template] },
|
127
|
+
});
|
128
|
+
}
|
129
|
+
return { type: 'get', ...action };
|
130
|
+
});
|
60
131
|
|
61
132
|
render(
|
62
133
|
<Provider store={store}>
|
63
134
|
<JobWizard />
|
64
135
|
</Provider>
|
65
136
|
);
|
66
|
-
const
|
67
|
-
|
137
|
+
const steps = [
|
138
|
+
'Target Hosts',
|
139
|
+
'Advanced Fields',
|
140
|
+
'Schedule',
|
141
|
+
'Review Details',
|
142
|
+
'Category and Template',
|
143
|
+
];
|
68
144
|
// eslint-disable-next-line no-unused-vars
|
69
145
|
for await (const step of steps) {
|
70
146
|
const stepSelector = screen.getByText(step);
|
@@ -1,12 +1,9 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { useSelector } from 'react-redux';
|
4
|
-
import { Form } from '@patternfly/react-core';
|
5
|
-
import {
|
6
|
-
|
7
|
-
selectAdvancedTemplateInputs,
|
8
|
-
selectTemplateInputs,
|
9
|
-
} from '../../JobWizardSelectors';
|
4
|
+
import { Title, Form } from '@patternfly/react-core';
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
6
|
+
import { selectJobTemplate } from '../../JobWizardSelectors';
|
10
7
|
import {
|
11
8
|
EffectiveUserField,
|
12
9
|
TimeoutToKillField,
|
@@ -15,29 +12,17 @@ import {
|
|
15
12
|
EffectiveUserPasswordField,
|
16
13
|
ConcurrencyLevelField,
|
17
14
|
TimeSpanLevelField,
|
18
|
-
TemplateInputsFields,
|
19
|
-
ExecutionOrderingField,
|
20
15
|
} from './Fields';
|
21
|
-
import { DescriptionField } from './DescriptionField';
|
22
|
-
import { WIZARD_TITLES } from '../../JobWizardConstants';
|
23
|
-
import { WizardTitle } from '../form/WizardTitle';
|
24
16
|
|
25
17
|
export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
|
26
|
-
const
|
27
|
-
const
|
28
|
-
const templateInputs = useSelector(selectTemplateInputs);
|
18
|
+
const jobTemplate = useSelector(selectJobTemplate);
|
19
|
+
const effectiveUser = jobTemplate.effective_user;
|
29
20
|
return (
|
30
21
|
<>
|
31
|
-
<
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
<Form id="advanced-fields-job-template" autoComplete="off">
|
36
|
-
<TemplateInputsFields
|
37
|
-
inputs={advancedTemplateInputs}
|
38
|
-
value={advancedValues.templateValues}
|
39
|
-
setValue={newValue => setAdvancedValues({ templateValues: newValue })}
|
40
|
-
/>
|
22
|
+
<Title headingLevel="h2" className="advanced-fields-title">
|
23
|
+
{__('Advanced Fields')}
|
24
|
+
</Title>
|
25
|
+
<Form>
|
41
26
|
{effectiveUser?.overridable && (
|
42
27
|
<EffectiveUserField
|
43
28
|
value={advancedValues.effectiveUserValue}
|
@@ -48,11 +33,6 @@ export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
|
|
48
33
|
}
|
49
34
|
/>
|
50
35
|
)}
|
51
|
-
<DescriptionField
|
52
|
-
inputs={templateInputs}
|
53
|
-
value={advancedValues.description}
|
54
|
-
setValue={newValue => setAdvancedValues({ description: newValue })}
|
55
|
-
/>
|
56
36
|
<TimeoutToKillField
|
57
37
|
value={advancedValues.timeoutToKill}
|
58
38
|
setValue={newValue =>
|
@@ -101,14 +81,6 @@ export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
|
|
101
81
|
})
|
102
82
|
}
|
103
83
|
/>
|
104
|
-
<ExecutionOrderingField
|
105
|
-
isRandomizedOrdering={advancedValues.isRandomizedOrdering}
|
106
|
-
setValue={newValue =>
|
107
|
-
setAdvancedValues({
|
108
|
-
isRandomizedOrdering: newValue,
|
109
|
-
})
|
110
|
-
}
|
111
|
-
/>
|
112
84
|
</Form>
|
113
85
|
</>
|
114
86
|
);
|