foreman_remote_execution 4.5.6 → 4.6.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/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
|
);
|