foreman_remote_execution 4.5.1 → 4.5.5

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/ui_job_wizard_controller.rb +7 -0
  3. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +5 -1
  4. data/app/helpers/remote_execution_helper.rb +9 -3
  5. data/app/lib/actions/remote_execution/run_hosts_job.rb +1 -1
  6. data/app/models/job_invocation_composer.rb +2 -2
  7. data/app/models/remote_execution_feature.rb +5 -1
  8. data/app/models/remote_execution_provider.rb +2 -1
  9. data/app/views/templates/ssh/module_action.erb +1 -0
  10. data/app/views/templates/ssh/power_action.erb +2 -0
  11. data/app/views/templates/ssh/puppet_run_once.erb +1 -0
  12. data/lib/foreman_remote_execution/version.rb +1 -1
  13. data/test/unit/remote_execution_provider_test.rb +12 -0
  14. data/webpack/JobWizard/JobWizard.js +28 -8
  15. data/webpack/JobWizard/JobWizard.scss +39 -0
  16. data/webpack/JobWizard/JobWizardConstants.js +10 -0
  17. data/webpack/JobWizard/JobWizardSelectors.js +9 -0
  18. data/webpack/JobWizard/__tests__/fixtures.js +104 -2
  19. data/webpack/JobWizard/__tests__/integration.test.js +13 -85
  20. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +21 -4
  21. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
  22. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +73 -59
  23. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +135 -16
  24. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
  25. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -51
  26. data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
  27. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
  28. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
  29. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
  30. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
  31. data/webpack/JobWizard/steps/Schedule/index.js +41 -0
  32. data/webpack/JobWizard/steps/form/FormHelpers.js +1 -0
  33. data/webpack/JobWizard/steps/form/Formatter.js +149 -0
  34. data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
  35. data/webpack/JobWizard/steps/form/SelectField.js +14 -2
  36. data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
  37. data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
  38. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
  39. metadata +13 -10
  40. data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -13
  41. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -32
  42. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +0 -249
  43. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -113
  44. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
  45. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
  46. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -37
  47. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -23
@@ -1,40 +1,27 @@
1
1
  import React from 'react';
2
2
  import { Provider } from 'react-redux';
3
- import configureMockStore from 'redux-mock-store';
4
3
  import { mount } from '@theforeman/test';
5
4
  import { render, fireEvent, screen, act } from '@testing-library/react';
6
5
  import * as api from 'foremanReact/redux/API';
7
6
  import { JobWizard } from '../JobWizard';
8
7
  import * as selectors from '../JobWizardSelectors';
9
- import { jobTemplates, jobTemplateResponse as jobTemplate } from './fixtures';
8
+ import {
9
+ testSetup,
10
+ mockApi,
11
+ jobCategories,
12
+ jobTemplateResponse as jobTemplate,
13
+ } from './fixtures';
10
14
 
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');
15
+ const store = testSetup(selectors, api);
16
16
 
17
- const jobCategories = ['Ansible Commands', 'Puppet', 'Services'];
17
+ selectors.selectJobTemplate.mockImplementation(() => {});
18
18
 
19
19
  api.get.mockImplementation(({ handleSuccess, ...action }) => {
20
20
  if (action.key === 'JOB_CATEGORIES') {
21
21
  handleSuccess && handleSuccess({ data: { job_categories: jobCategories } });
22
- } else if (action.key === 'JOB_TEMPLATE') {
23
- handleSuccess &&
24
- handleSuccess({
25
- data: jobTemplate,
26
- });
27
22
  }
28
23
  return { type: 'get', ...action };
29
24
  });
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({});
38
25
  describe('Job wizard fill', () => {
39
26
  it('should select template', async () => {
40
27
  const wrapper = mount(
@@ -51,7 +38,10 @@ describe('Job wizard fill', () => {
51
38
  selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
52
39
  wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
53
40
  await act(async () => {
54
- await wrapper.find('.pf-c-select__menu-item').simulate('click');
41
+ await wrapper
42
+ .find('.pf-c-select__menu-item')
43
+ .first()
44
+ .simulate('click');
55
45
  await wrapper.update();
56
46
  });
57
47
  expect(store.getActions().slice(-1)).toMatchSnapshot('select template');
@@ -60,74 +50,12 @@ describe('Job wizard fill', () => {
60
50
  );
61
51
  });
62
52
 
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
-
109
53
  it('have all steps', async () => {
110
54
  selectors.selectJobCategoriesStatus.mockImplementation(() => null);
111
55
  selectors.selectJobTemplate.mockRestore();
112
56
  selectors.selectJobTemplates.mockRestore();
113
57
  selectors.selectJobCategories.mockRestore();
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
- });
58
+ mockApi(api);
131
59
 
132
60
  render(
133
61
  <Provider store={store}>
@@ -3,7 +3,11 @@ import PropTypes from 'prop-types';
3
3
  import { useSelector } from 'react-redux';
4
4
  import { Title, Form } from '@patternfly/react-core';
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
- import { selectJobTemplate } from '../../JobWizardSelectors';
6
+ import {
7
+ selectEffectiveUser,
8
+ selectAdvancedTemplateInputs,
9
+ selectTemplateInputs,
10
+ } from '../../JobWizardSelectors';
7
11
  import {
8
12
  EffectiveUserField,
9
13
  TimeoutToKillField,
@@ -12,17 +16,25 @@ import {
12
16
  EffectiveUserPasswordField,
13
17
  ConcurrencyLevelField,
14
18
  TimeSpanLevelField,
19
+ TemplateInputsFields,
15
20
  } from './Fields';
21
+ import { DescriptionField } from './DescriptionField';
16
22
 
17
23
  export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
18
- const jobTemplate = useSelector(selectJobTemplate);
19
- const effectiveUser = jobTemplate.effective_user;
24
+ const effectiveUser = useSelector(selectEffectiveUser);
25
+ const advancedTemplateInputs = useSelector(selectAdvancedTemplateInputs);
26
+ const templateInputs = useSelector(selectTemplateInputs);
20
27
  return (
21
28
  <>
22
29
  <Title headingLevel="h2" className="advanced-fields-title">
23
30
  {__('Advanced Fields')}
24
31
  </Title>
25
- <Form>
32
+ <Form id="advanced-fields-job-template" autoComplete="off">
33
+ <TemplateInputsFields
34
+ inputs={advancedTemplateInputs}
35
+ value={advancedValues.templateValues}
36
+ setValue={newValue => setAdvancedValues({ templateValues: newValue })}
37
+ />
26
38
  {effectiveUser?.overridable && (
27
39
  <EffectiveUserField
28
40
  value={advancedValues.effectiveUserValue}
@@ -33,6 +45,11 @@ export const AdvancedFields = ({ advancedValues, setAdvancedValues }) => {
33
45
  }
34
46
  />
35
47
  )}
48
+ <DescriptionField
49
+ inputs={templateInputs}
50
+ value={advancedValues.description}
51
+ setValue={newValue => setAdvancedValues({ description: newValue })}
52
+ />
36
53
  <TimeoutToKillField
37
54
  value={advancedValues.timeoutToKill}
38
55
  setValue={newValue =>
@@ -0,0 +1,67 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FormGroup, TextInput, Button } from '@patternfly/react-core';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ export const DescriptionField = ({ inputs, value, setValue }) => {
7
+ const generateDesc = () => {
8
+ let newDesc = value;
9
+ if (value) {
10
+ const re = new RegExp('%\\{([^\\}]+)\\}', 'gm');
11
+ const results = [...newDesc.matchAll(re)].map(result => ({
12
+ name: result[1],
13
+ text: result[0],
14
+ }));
15
+ results.forEach(result => {
16
+ newDesc = newDesc.replace(
17
+ result.text,
18
+ // TODO: Replace with the value of the input from Target Hosts step
19
+ inputs.find(input => input.name === result.name)?.name || result.text
20
+ );
21
+ });
22
+ }
23
+ return newDesc;
24
+ };
25
+ const [generatedDesc, setGeneratedDesc] = useState(generateDesc());
26
+ const [isPreview, setIsPreview] = useState(true);
27
+
28
+ const togglePreview = () => {
29
+ setGeneratedDesc(generateDesc());
30
+ setIsPreview(v => !v);
31
+ };
32
+
33
+ return (
34
+ <FormGroup
35
+ label={__('Description')}
36
+ fieldId="description"
37
+ helperText={
38
+ <Button variant="link" isInline onClick={togglePreview}>
39
+ {isPreview
40
+ ? __('Edit job description template')
41
+ : __('Preview job description')}
42
+ </Button>
43
+ }
44
+ >
45
+ {isPreview ? (
46
+ <TextInput id="description-preview" value={generatedDesc} isDisabled />
47
+ ) : (
48
+ <TextInput
49
+ type="text"
50
+ autoComplete="description"
51
+ id="description"
52
+ value={value}
53
+ onChange={newValue => setValue(newValue)}
54
+ />
55
+ )}
56
+ </FormGroup>
57
+ );
58
+ };
59
+
60
+ DescriptionField.propTypes = {
61
+ inputs: PropTypes.array.isRequired,
62
+ value: PropTypes.string,
63
+ setValue: PropTypes.func.isRequired,
64
+ };
65
+ DescriptionField.defaultProps = {
66
+ value: '',
67
+ };
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
3
3
  import { FormGroup, TextInput } from '@patternfly/react-core';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
5
  import { helpLabel } from '../form/FormHelpers';
6
+ import { formatter } from '../form/Formatter';
7
+ import { NumberInput } from '../form/NumberInput';
6
8
 
7
9
  export const EffectiveUserField = ({ value, setValue }) => (
8
10
  <FormGroup
@@ -26,25 +28,25 @@ export const EffectiveUserField = ({ value, setValue }) => (
26
28
  );
27
29
 
28
30
  export const TimeoutToKillField = ({ value, setValue }) => (
29
- <FormGroup
30
- label={__('Timeout to kill')}
31
- labelIcon={helpLabel(
32
- __(
33
- 'Time in seconds from the start on the remote host after which the job should be killed.'
31
+ <NumberInput
32
+ formProps={{
33
+ label: __('Timeout to kill'),
34
+ labelIcon: helpLabel(
35
+ __(
36
+ 'Time in seconds from the start on the remote host after which the job should be killed.'
37
+ ),
38
+ 'timeout-to-kill'
34
39
  ),
35
- 'timeout-to-kill'
36
- )}
37
- fieldId="timeout-to-kill"
38
- >
39
- <TextInput
40
- type="number"
41
- value={value}
42
- placeholder={__('For example: 1, 2, 3, 4, 5...')}
43
- autoComplete="timeout-to-kill"
44
- id="timeout-to-kill"
45
- onChange={newValue => setValue(newValue)}
46
- />
47
- </FormGroup>
40
+ fieldId: 'timeout-to-kill',
41
+ }}
42
+ inputProps={{
43
+ value,
44
+ placeholder: __('For example: 1, 2, 3, 4, 5...'),
45
+ autoComplete: 'timeout-to-kill',
46
+ id: 'timeout-to-kill',
47
+ onChange: newValue => setValue(newValue),
48
+ }}
49
+ />
48
50
  );
49
51
 
50
52
  export const PasswordField = ({ value, setValue }) => (
@@ -56,11 +58,11 @@ export const PasswordField = ({ value, setValue }) => (
56
58
  ),
57
59
  'password'
58
60
  )}
59
- fieldId="password"
61
+ fieldId="job-password"
60
62
  >
61
63
  <TextInput
62
- autoComplete="password"
63
- id="password"
64
+ autoComplete="new-password" // to prevent firefox from autofilling the user password
65
+ id="job-password"
64
66
  type="password"
65
67
  placeholder="*****"
66
68
  value={value}
@@ -114,51 +116,54 @@ export const EffectiveUserPasswordField = ({ value, setValue }) => (
114
116
  );
115
117
 
116
118
  export const ConcurrencyLevelField = ({ value, setValue }) => (
117
- <FormGroup
118
- label={__('Concurrency level')}
119
- labelIcon={helpLabel(
120
- __(
121
- 'Run at most N tasks at a time. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
119
+ <NumberInput
120
+ formProps={{
121
+ label: __('Concurrency level'),
122
+ labelIcon: helpLabel(
123
+ __(
124
+ 'Run at most N tasks at a time. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
125
+ ),
126
+ 'concurrency-level'
122
127
  ),
123
- 'concurrency-level'
124
- )}
125
- fieldId="concurrency-level"
126
- >
127
- <TextInput
128
- min={1}
129
- type="number"
130
- autoComplete="concurrency-level"
131
- id="concurrency-level"
132
- placeholder={__('For example: 1, 2, 3, 4, 5...')}
133
- value={value}
134
- onChange={newValue => setValue(newValue)}
135
- />
136
- </FormGroup>
128
+ fieldId: 'concurrency-level',
129
+ }}
130
+ inputProps={{
131
+ min: 1,
132
+ autoComplete: 'concurrency-level',
133
+ id: 'concurrency-level',
134
+ placeholder: __('For example: 1, 2, 3, 4, 5...'),
135
+ value,
136
+ onChange: newValue => setValue(newValue),
137
+ }}
138
+ />
137
139
  );
138
140
 
139
141
  export const TimeSpanLevelField = ({ value, setValue }) => (
140
- <FormGroup
141
- label={__('Time span')}
142
- labelIcon={helpLabel(
143
- __(
144
- 'Distribute execution over N seconds. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
142
+ <NumberInput
143
+ formProps={{
144
+ label: __('Time span'),
145
+ labelIcon: helpLabel(
146
+ __(
147
+ 'Distribute execution over N seconds. If this is set and proxy batch triggering is enabled, then tasks are triggered on the smart proxy in batches of size 1.'
148
+ ),
149
+ 'time-span'
145
150
  ),
146
- 'time-span'
147
- )}
148
- fieldId="time-span"
149
- >
150
- <TextInput
151
- min={1}
152
- type="number"
153
- autoComplete="time-span"
154
- id="time-span"
155
- placeholder={__('For example: 1, 2, 3, 4, 5...')}
156
- value={value}
157
- onChange={newValue => setValue(newValue)}
158
- />
159
- </FormGroup>
151
+ fieldId: 'time-span',
152
+ }}
153
+ inputProps={{
154
+ min: 1,
155
+ autoComplete: 'time-span',
156
+ id: 'time-span',
157
+ placeholder: __('For example: 1, 2, 3, 4, 5...'),
158
+ value,
159
+ onChange: newValue => setValue(newValue),
160
+ }}
161
+ />
160
162
  );
161
163
 
164
+ export const TemplateInputsFields = ({ inputs, value, setValue }) => (
165
+ <>{inputs?.map(input => formatter(input, value, setValue))}</>
166
+ );
162
167
  EffectiveUserField.propTypes = {
163
168
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
164
169
  setValue: PropTypes.func.isRequired,
@@ -179,3 +184,12 @@ ConcurrencyLevelField.propTypes = EffectiveUserField.propTypes;
179
184
  ConcurrencyLevelField.defaultProps = EffectiveUserField.defaultProps;
180
185
  TimeSpanLevelField.propTypes = EffectiveUserField.propTypes;
181
186
  TimeSpanLevelField.defaultProps = EffectiveUserField.defaultProps;
187
+ TemplateInputsFields.propTypes = {
188
+ inputs: PropTypes.array.isRequired,
189
+ value: PropTypes.object,
190
+ setValue: PropTypes.func.isRequired,
191
+ };
192
+
193
+ TemplateInputsFields.defaultProps = {
194
+ value: {},
195
+ };
@@ -1,25 +1,144 @@
1
1
  import React from 'react';
2
2
  import { Provider } from 'react-redux';
3
- import configureMockStore from 'redux-mock-store';
4
- import * as patternfly from '@patternfly/react-core';
5
3
  import { mount } from '@theforeman/test';
6
- import { AdvancedFields } from '../AdvancedFields';
7
-
8
- jest.spyOn(patternfly, 'FormGroup');
9
- patternfly.FormGroup.mockImplementation(props => (
10
- <div>{props.navAriaLabel}</div>
11
- ));
12
- const mockStore = configureMockStore([]);
13
- const store = mockStore({
14
- JOB_TEMPLATE: { response: { effective_user: { overridable: true } } },
15
- });
4
+ import { fireEvent, screen, render, act } from '@testing-library/react';
5
+ import * as api from 'foremanReact/redux/API';
6
+ import { JobWizard } from '../../../JobWizard';
7
+ import * as selectors from '../../../JobWizardSelectors';
8
+ import {
9
+ jobTemplateResponse as jobTemplate,
10
+ testSetup,
11
+ mockApi,
12
+ } from '../../../__tests__/fixtures';
13
+
14
+ const store = testSetup(selectors, api);
15
+ mockApi(api);
16
+
17
+ jest.spyOn(selectors, 'selectEffectiveUser');
18
+ jest.spyOn(selectors, 'selectTemplateInputs');
19
+ jest.spyOn(selectors, 'selectAdvancedTemplateInputs');
20
+
21
+ selectors.selectEffectiveUser.mockImplementation(
22
+ () => jobTemplate.effective_user
23
+ );
24
+ selectors.selectTemplateInputs.mockImplementation(
25
+ () => jobTemplate.template_inputs
26
+ );
27
+
28
+ selectors.selectAdvancedTemplateInputs.mockImplementation(
29
+ () => jobTemplate.advanced_template_inputs
30
+ );
16
31
  describe('AdvancedFields', () => {
17
- it('rendring', () => {
18
- const component = mount(
32
+ it('should save data between steps for advanced fields', async () => {
33
+ const wrapper = mount(
19
34
  <Provider store={store}>
20
- <AdvancedFields advancedValues={{}} setAdvancedValues={jest.fn()} />
35
+ <JobWizard advancedValues={{}} setAdvancedValues={jest.fn()} />
21
36
  </Provider>
22
37
  );
23
- expect(component).toMatchSnapshot();
38
+ // setup
39
+ wrapper.find('.pf-c-button.pf-c-select__toggle-button').simulate('click');
40
+ wrapper
41
+ .find('.pf-c-select__menu-item')
42
+ .first()
43
+ .simulate('click');
44
+
45
+ // test
46
+ expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-disabled')).toHaveLength(
47
+ 0
48
+ );
49
+ wrapper
50
+ .find('.pf-c-wizard__nav-link')
51
+ .at(2)
52
+ .simulate('click'); // Advanced step
53
+ const effectiveUserInput = () => wrapper.find('input#effective-user');
54
+ const advancedTemplateInput = () =>
55
+ wrapper.find('.pf-c-form__group-control textarea');
56
+ const effectiveUesrValue = 'effective user new value';
57
+ const advancedTemplateInputValue = 'advanced input new value';
58
+ effectiveUserInput().getDOMNode().value = effectiveUesrValue;
59
+
60
+ effectiveUserInput().simulate('change');
61
+ wrapper.update();
62
+ advancedTemplateInput().getDOMNode().value = advancedTemplateInputValue;
63
+ advancedTemplateInput().simulate('change');
64
+ expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
65
+ expect(advancedTemplateInput().prop('value')).toEqual(
66
+ advancedTemplateInputValue
67
+ );
68
+
69
+ wrapper
70
+ .find('.pf-c-wizard__nav-link')
71
+ .at(1)
72
+ .simulate('click');
73
+
74
+ expect(wrapper.find('.pf-c-wizard__nav-link.pf-m-current').text()).toEqual(
75
+ 'Target Hosts'
76
+ );
77
+ wrapper
78
+ .find('.pf-c-wizard__nav-link')
79
+ .at(2)
80
+ .simulate('click'); // Advanced step
81
+
82
+ expect(effectiveUserInput().prop('value')).toEqual(effectiveUesrValue);
83
+ expect(advancedTemplateInput().prop('value')).toEqual(
84
+ advancedTemplateInputValue
85
+ );
86
+ });
87
+ it('fill template fields', async () => {
88
+ render(
89
+ <Provider store={store}>
90
+ <JobWizard />
91
+ </Provider>
92
+ );
93
+ await act(async () => {
94
+ fireEvent.click(screen.getByText('Advanced Fields'));
95
+ });
96
+ const searchValue = 'search test';
97
+ const textValue = 'I am a text';
98
+ const dateValue = '08/07/2021';
99
+ const textField = screen.getByLabelText('adv plain hidden', {
100
+ selector: 'textarea',
101
+ });
102
+ const selectField = screen.getByText('option 1');
103
+ const searchField = screen.getByPlaceholderText('Filter...');
104
+ const dateField = screen.getByLabelText('adv date', {
105
+ selector: 'input',
106
+ });
107
+
108
+ fireEvent.click(selectField);
109
+ await act(async () => {
110
+ await fireEvent.click(screen.getByText('option 2'));
111
+ fireEvent.click(screen.getAllByText('Advanced Fields')[0]); // to remove focus
112
+ await fireEvent.change(textField, {
113
+ target: { value: textValue },
114
+ });
115
+
116
+ await fireEvent.change(searchField, {
117
+ target: { value: searchValue },
118
+ });
119
+ await fireEvent.change(dateField, {
120
+ target: { value: dateValue },
121
+ });
122
+ });
123
+ expect(
124
+ screen.getByLabelText('adv plain hidden', {
125
+ selector: 'textarea',
126
+ }).value
127
+ ).toBe(textValue);
128
+ expect(searchField.value).toBe(searchValue);
129
+ expect(dateField.value).toBe(dateValue);
130
+ await act(async () => {
131
+ fireEvent.click(screen.getByText('Category and Template'));
132
+ });
133
+ expect(screen.getAllByText('Category and Template')).toHaveLength(3);
134
+
135
+ await act(async () => {
136
+ fireEvent.click(screen.getByText('Advanced Fields'));
137
+ });
138
+ expect(textField.value).toBe(textValue);
139
+ expect(searchField.value).toBe(searchValue);
140
+ expect(dateField.value).toBe(dateValue);
141
+ expect(screen.queryAllByText('option 1')).toHaveLength(0);
142
+ expect(screen.queryAllByText('option 2')).toHaveLength(1);
24
143
  });
25
144
  });