foreman_remote_execution 4.4.0 → 4.5.3

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +13 -24
  3. data/app/controllers/job_invocations_controller.rb +1 -1
  4. data/app/controllers/job_templates_controller.rb +4 -4
  5. data/app/controllers/ui_job_wizard_controller.rb +19 -0
  6. data/app/helpers/job_invocations_helper.rb +2 -2
  7. data/app/helpers/remote_execution_helper.rb +13 -9
  8. data/app/lib/actions/remote_execution/run_host_job.rb +36 -6
  9. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +7 -5
  10. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +6 -0
  11. data/app/models/host_proxy_invocation.rb +4 -0
  12. data/app/models/host_status/execution_status.rb +5 -5
  13. data/app/models/job_invocation.rb +31 -12
  14. data/app/models/job_invocation_composer.rb +61 -19
  15. data/app/models/remote_execution_provider.rb +1 -1
  16. data/app/models/setting/remote_execution.rb +2 -2
  17. data/app/models/ssh_execution_provider.rb +4 -4
  18. data/app/models/targeting.rb +5 -1
  19. data/app/overrides/execution_interface.rb +8 -8
  20. data/app/overrides/subnet_proxies.rb +6 -6
  21. data/app/views/job_invocations/index.html.erb +1 -1
  22. data/app/views/templates/ssh/module_action.erb +1 -0
  23. data/app/views/templates/ssh/puppet_run_once.erb +1 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
  26. data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
  27. data/db/migrate/2021051713291621250977_add_host_proxy_invocations.rb +12 -0
  28. data/extra/cockpit/foreman-cockpit-session +6 -6
  29. data/lib/foreman_remote_execution/engine.rb +11 -8
  30. data/lib/foreman_remote_execution/version.rb +1 -1
  31. data/package.json +2 -1
  32. data/test/functional/api/v2/job_invocations_controller_test.rb +14 -1
  33. data/test/unit/job_invocation_composer_test.rb +59 -2
  34. data/test/unit/job_invocation_test.rb +1 -1
  35. data/webpack/JobWizard/JobWizard.js +80 -19
  36. data/webpack/JobWizard/JobWizard.scss +42 -1
  37. data/webpack/JobWizard/JobWizardConstants.js +11 -0
  38. data/webpack/JobWizard/JobWizardSelectors.js +27 -1
  39. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
  40. data/webpack/JobWizard/__tests__/fixtures.js +128 -0
  41. data/webpack/JobWizard/__tests__/integration.test.js +84 -0
  42. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +110 -0
  43. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +67 -0
  44. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +195 -0
  45. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +144 -0
  46. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +23 -0
  47. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +34 -2
  48. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +122 -44
  49. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +9 -1
  50. data/webpack/JobWizard/steps/Schedule/QueryType.js +48 -0
  51. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +61 -0
  52. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +25 -0
  53. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +51 -0
  54. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +22 -0
  55. data/webpack/JobWizard/steps/Schedule/index.js +41 -0
  56. data/webpack/JobWizard/steps/form/FormHelpers.js +20 -0
  57. data/webpack/JobWizard/steps/form/Formatter.js +149 -0
  58. data/webpack/JobWizard/steps/form/GroupedSelectField.js +3 -0
  59. data/webpack/JobWizard/steps/form/NumberInput.js +33 -0
  60. data/webpack/JobWizard/steps/form/SelectField.js +24 -3
  61. data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +76 -0
  62. data/webpack/__mocks__/foremanReact/components/SearchBar.js +18 -1
  63. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
  64. data/webpack/global_index.js +5 -3
  65. data/webpack/index.js +3 -0
  66. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +1 -5
  67. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
  68. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -0
  69. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
  70. data/webpack/react_app/extend/{fills.js → fillRecentJobsCard.js} +7 -6
  71. data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
  72. data/webpack/react_app/extend/reducers.js +2 -1
  73. metadata +24 -14
  74. data/app/models/concerns/foreman_remote_execution/orchestration/ssh.rb +0 -70
  75. data/test/models/orchestration/ssh_test.rb +0 -56
  76. data/webpack/JobWizard/__tests__/JobWizard.test.js +0 -20
  77. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +0 -83
  78. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +0 -64
  79. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +0 -38
  80. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +0 -23
  81. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +0 -36
  82. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +0 -22
  83. data/webpack/fills_index.js +0 -11
@@ -622,7 +622,7 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
622
622
  end
623
623
  end
624
624
 
625
- describe '#from_api_params' do
625
+ describe '.from_api_params' do
626
626
  let(:composer) do
627
627
  JobInvocationComposer.from_api_params(params)
628
628
  end
@@ -754,12 +754,23 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
754
754
  assert composer.save!
755
755
  _(composer.job_invocation.remote_execution_feature).must_equal feature
756
756
  end
757
+
758
+ it 'sets the remote execution_feature id based on `feature` param' do
759
+ params[:remote_execution_feature_id] = nil
760
+ params[:feature] = feature.label
761
+ params[:job_template_id] = trying_job_template_1.id
762
+ refute_equal feature.job_template, trying_job_template_1
763
+
764
+ assert composer.save!
765
+ _(composer.job_invocation.remote_execution_feature).must_equal feature
766
+ end
757
767
  end
758
768
 
759
769
  context 'with invalid targeting' do
760
770
  let(:params) do
761
771
  { :job_category => trying_job_template_1.job_category,
762
772
  :job_template_id => trying_job_template_1.id,
773
+ :targeting_type => 'fake',
763
774
  :search_query => 'some hosts',
764
775
  :inputs => {input1.name => 'some_value'}}
765
776
  end
@@ -853,7 +864,9 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
853
864
  it 'marks targeting as resolved if static' do
854
865
  created = JobInvocationComposer.from_job_invocation(job_invocation).job_invocation
855
866
  assert created.targeting.resolved?
856
- assert_equal job_invocation.template_invocations_host_ids, created.targeting.host_ids
867
+ created.targeting.save
868
+ created.targeting.reload
869
+ assert_equal job_invocation.template_invocations_host_ids, created.targeting.targeting_hosts.pluck(:host_id)
857
870
  end
858
871
 
859
872
  it 'takes randomized_ordering from the original job invocation when rerunning failed' do
@@ -863,6 +876,50 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
863
876
  composer = JobInvocationComposer.from_job_invocation(job_invocation, :host_ids => host_ids)
864
877
  assert composer.job_invocation.targeting.randomized_ordering
865
878
  end
879
+
880
+ it 'works with invalid hosts' do
881
+ host = job_invocation.targeting.hosts.first
882
+ ::Host::Managed.any_instance.stubs(:valid?).returns(false)
883
+ composer = JobInvocationComposer.from_job_invocation(job_invocation, {})
884
+ targeting = composer.compose.job_invocation.targeting
885
+ targeting.save!
886
+ targeting.reload
887
+ assert targeting.valid?
888
+ assert_equal targeting.hosts.pluck(:id), [host.id]
889
+ end
890
+ end
891
+
892
+ describe '.for_feature' do
893
+ let(:feature) { FactoryBot.create(:remote_execution_feature, job_template: trying_job_template_1) }
894
+ let(:host) { FactoryBot.create(:host) }
895
+ let(:bookmark) { Bookmark.create!(:query => 'b', :name => 'bookmark', :public => true, :controller => 'hosts') }
896
+
897
+ context 'specifying hosts' do
898
+ it 'takes a bookmarked search' do
899
+ composer = JobInvocationComposer.for_feature(feature.label, bookmark, {})
900
+ assert_equal bookmark.id, composer.params['targeting']['bookmark_id']
901
+ end
902
+
903
+ it 'takes an array of host ids' do
904
+ composer = JobInvocationComposer.for_feature(feature.label, [host.id], {})
905
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
906
+ end
907
+
908
+ it 'takes a single host object' do
909
+ composer = JobInvocationComposer.for_feature(feature.label, host, {})
910
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
911
+ end
912
+
913
+ it 'takes an array of host FQDNs' do
914
+ composer = JobInvocationComposer.for_feature(feature.label, [host.fqdn], {})
915
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
916
+ end
917
+
918
+ it 'takes a search query string' do
919
+ composer = JobInvocationComposer.for_feature(feature.label, 'host.example.com', {})
920
+ assert_equal 'host.example.com', composer.search_query
921
+ end
922
+ end
866
923
  end
867
924
 
868
925
  describe '#resolve_job_category and #resolve job_templates' do
@@ -10,7 +10,7 @@ class JobInvocationTest < ActiveSupport::TestCase
10
10
  end
11
11
 
12
12
  it 'is able to perform search through job invocations' do
13
- found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).with_task.order('job_invocations.id DESC')
13
+ found_jobs = JobInvocation.search_for(%{job_category = "#{job_invocation.job_category}"}).paginate(:page => 1).order('job_invocations.id DESC')
14
14
  _(found_jobs).must_equal [job_invocation]
15
15
  end
16
16
 
@@ -1,55 +1,116 @@
1
- import React, { useState } from 'react';
1
+ /* eslint-disable camelcase */
2
+ import React, { useState, useEffect, useCallback } from 'react';
3
+ import { useDispatch, useSelector } from 'react-redux';
2
4
  import { Wizard } from '@patternfly/react-core';
5
+ import { get } from 'foremanReact/redux/API';
3
6
  import { translate as __ } from 'foremanReact/common/I18n';
4
7
  import history from 'foremanReact/history';
5
8
  import CategoryAndTemplate from './steps/CategoryAndTemplate/';
9
+ import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
10
+ import { JOB_TEMPLATE } from './JobWizardConstants';
11
+ import { selectTemplateError } from './JobWizardSelectors';
12
+ import Schedule from './steps/Schedule/';
6
13
  import './JobWizard.scss';
7
14
 
8
15
  export const JobWizard = () => {
9
- const [jobTemplate, setJobTemplate] = useState(null);
16
+ const [jobTemplateID, setJobTemplateID] = useState(null);
10
17
  const [category, setCategory] = useState('');
18
+ const [advancedValues, setAdvancedValues] = useState({});
19
+ const dispatch = useDispatch();
20
+
21
+ const setDefaults = useCallback(
22
+ ({
23
+ data: {
24
+ advanced_template_inputs,
25
+ effective_user,
26
+ job_template: { executionTimeoutInterval, description_format },
27
+ },
28
+ }) => {
29
+ const advancedTemplateValues = {};
30
+ const advancedInputs = advanced_template_inputs;
31
+ if (advancedInputs) {
32
+ advancedInputs.forEach(input => {
33
+ advancedTemplateValues[input.name] = input?.default || '';
34
+ });
35
+ }
36
+ setAdvancedValues(currentAdvancedValues => ({
37
+ ...currentAdvancedValues,
38
+ effectiveUserValue: effective_user?.value || '',
39
+ timeoutToKill: executionTimeoutInterval || '',
40
+ templateValues: advancedTemplateValues,
41
+ description: description_format || '',
42
+ }));
43
+ },
44
+ []
45
+ );
46
+ useEffect(() => {
47
+ if (jobTemplateID) {
48
+ dispatch(
49
+ get({
50
+ key: JOB_TEMPLATE,
51
+ url: `/ui_job_wizard/template/${jobTemplateID}`,
52
+ handleSuccess: setDefaults,
53
+ })
54
+ );
55
+ }
56
+ }, [jobTemplateID, setDefaults, dispatch]);
57
+
58
+ const templateError = !!useSelector(selectTemplateError);
59
+ const isTemplate = !templateError && !!jobTemplateID;
11
60
  const steps = [
12
61
  {
13
- name: __('Category and template'),
62
+ name: __('Category and Template'),
14
63
  component: (
15
64
  <CategoryAndTemplate
16
- jobTemplate={jobTemplate}
17
- setJobTemplate={setJobTemplate}
65
+ jobTemplate={jobTemplateID}
66
+ setJobTemplate={setJobTemplateID}
18
67
  category={category}
19
68
  setCategory={setCategory}
20
69
  />
21
70
  ),
22
71
  },
23
72
  {
24
- name: __('Target hosts'),
25
- component: <p>TargetHosts </p>,
26
- canJumpTo: !!jobTemplate,
73
+ name: __('Target Hosts'),
74
+ component: <p>Target Hosts</p>,
75
+ canJumpTo: isTemplate,
27
76
  },
28
77
  {
29
- name: __('Advanced fields'),
30
- component: <p> AdvancedFields </p>,
31
- canJumpTo: !!jobTemplate,
78
+ name: __('Advanced Fields'),
79
+ component: (
80
+ <AdvancedFields
81
+ advancedValues={advancedValues}
82
+ setAdvancedValues={newValues => {
83
+ setAdvancedValues(currentAdvancedValues => ({
84
+ ...currentAdvancedValues,
85
+ ...newValues,
86
+ }));
87
+ }}
88
+ jobTemplateID={jobTemplateID}
89
+ />
90
+ ),
91
+ canJumpTo: isTemplate,
32
92
  },
33
93
  {
34
94
  name: __('Schedule'),
35
- component: <p>Schedule</p>,
36
- canJumpTo: !!jobTemplate,
95
+ component: <Schedule />,
96
+ canJumpTo: isTemplate,
37
97
  },
38
98
  {
39
- name: __('Review details'),
40
- component: <p>ReviewDetails</p>,
99
+ name: __('Review Details'),
100
+ component: <p>Review Details</p>,
41
101
  nextButtonText: 'Run',
42
- canJumpTo: !!jobTemplate,
102
+ canJumpTo: isTemplate,
43
103
  },
44
104
  ];
45
- const title = __('Run Job');
46
105
  return (
47
106
  <Wizard
48
107
  onClose={() => history.goBack()}
49
- navAriaLabel={`${title} steps`}
108
+ navAriaLabel="Run Job steps"
50
109
  steps={steps}
51
- height="70vh"
110
+ height="100%"
52
111
  className="job-wizard"
53
112
  />
54
113
  );
55
114
  };
115
+
116
+ export default JobWizard;
@@ -1,6 +1,5 @@
1
1
  .job-wizard {
2
2
  .pf-c-wizard__main {
3
- overflow: visible;
4
3
  z-index: calc(
5
4
  var(--pf-c-wizard__footer--ZIndex) + 1
6
5
  ); // So the select box can be shown above the wizard footer
@@ -8,5 +7,47 @@
8
7
 
9
8
  .pf-c-wizard__main-body {
10
9
  max-width: 500px;
10
+ .advanced-fields-title {
11
+ margin-bottom: 10px;
12
+ }
13
+ #advanced-fields-job-template {
14
+ .foreman-search-field {
15
+ // Giving pf3 search bar a pf4 look
16
+ .search-bar {
17
+ display: block;
18
+ }
19
+ .input-group-btn {
20
+ display: none;
21
+ }
22
+ li {
23
+ font-size: 16px;
24
+ }
25
+ input {
26
+ font-size: 16px;
27
+ height: 36px;
28
+ }
29
+ .foreman-autocomplete .autocomplete-focus-shortcut {
30
+ top: 8px;
31
+ font-size: 16px;
32
+ }
33
+ .foreman-autocomplete .autocomplete-aux {
34
+ top: 8px;
35
+ font-size: 16px;
36
+ .autocomplete-clear-button {
37
+ font-size: 16px;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ .schedule-tab {
45
+ input[type='radio'],
46
+ input[type='checkbox'] {
47
+ margin: 0;
48
+ }
49
+ .advanced-scheduling-button {
50
+ text-align: start;
51
+ }
11
52
  }
12
53
  }
@@ -1,5 +1,16 @@
1
+ import { translate as __ } from 'foremanReact/common/I18n';
1
2
  import { foremanUrl } from 'foremanReact/common/helpers';
2
3
 
3
4
  export const JOB_TEMPLATES = 'JOB_TEMPLATES';
4
5
  export const JOB_CATEGORIES = 'JOB_CATEGORIES';
6
+ export const JOB_TEMPLATE = 'JOB_TEMPLATE';
5
7
  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
+ };
@@ -1,9 +1,14 @@
1
1
  import {
2
2
  selectAPIResponse,
3
3
  selectAPIStatus,
4
+ selectAPIErrorMessage,
4
5
  } from 'foremanReact/redux/API/APISelectors';
5
6
 
6
- import { JOB_TEMPLATES, JOB_CATEGORIES } from './JobWizardConstants';
7
+ import {
8
+ JOB_TEMPLATES,
9
+ JOB_CATEGORIES,
10
+ JOB_TEMPLATE,
11
+ } from './JobWizardConstants';
7
12
 
8
13
  export const selectJobTemplatesStatus = state =>
9
14
  selectAPIStatus(state, JOB_TEMPLATES);
@@ -19,3 +24,24 @@ export const selectJobCategories = state =>
19
24
 
20
25
  export const selectJobCategoriesStatus = state =>
21
26
  selectAPIStatus(state, JOB_CATEGORIES);
27
+
28
+ export const selectCategoryError = state =>
29
+ selectAPIErrorMessage(state, JOB_CATEGORIES);
30
+
31
+ export const selectAllTemplatesError = state =>
32
+ selectAPIErrorMessage(state, JOB_TEMPLATES);
33
+
34
+ export const selectTemplateError = state =>
35
+ selectAPIErrorMessage(state, JOB_TEMPLATE);
36
+
37
+ export const selectJobTemplate = state =>
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,43 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Job wizard fill should select template: initial 1`] = `
4
+ Array [
5
+ Object {
6
+ "key": "JOB_CATEGORIES",
7
+ "type": "get",
8
+ "url": "/ui_job_wizard/categories",
9
+ },
10
+ Object {
11
+ "key": "JOB_TEMPLATES",
12
+ "type": "get",
13
+ "url": URI {
14
+ "_deferred_build": true,
15
+ "_parts": Object {
16
+ "duplicateQueryParameters": false,
17
+ "escapeQuerySpace": true,
18
+ "fragment": null,
19
+ "hostname": null,
20
+ "password": null,
21
+ "path": "foreman/api/v2/job_templates",
22
+ "port": null,
23
+ "preventInvalidHostname": false,
24
+ "protocol": null,
25
+ "query": "search=job_category%3D%22Ansible+Commands%22&per_page=all",
26
+ "urn": null,
27
+ "username": null,
28
+ },
29
+ "_string": "",
30
+ },
31
+ },
32
+ ]
33
+ `;
34
+
35
+ exports[`Job wizard fill should select template: select template 1`] = `
36
+ Array [
37
+ Object {
38
+ "key": "JOB_TEMPLATE",
39
+ "type": "get",
40
+ "url": "/ui_job_wizard/template/178",
41
+ },
42
+ ]
43
+ `;
@@ -0,0 +1,128 @@
1
+ import configureMockStore from 'redux-mock-store';
2
+
3
+ export const jobTemplate = {
4
+ id: 178,
5
+ name: 'template1',
6
+ template:
7
+ "---\n- hosts: all\n tasks:\n - shell:\n cmd: |\n<%= indent(10) { input('command') } %>\n register: out\n - debug: var=out",
8
+ snippet: false,
9
+ default: true,
10
+ job_category: 'Ansible Commands',
11
+ provider_type: 'Ansible',
12
+ description_format: 'Run %{command}',
13
+ execution_timeout_interval: 2,
14
+ description: null,
15
+ };
16
+
17
+ export const jobTemplates = [jobTemplate];
18
+
19
+ export const jobTemplateResponse = {
20
+ job_template: jobTemplate,
21
+ effective_user: {
22
+ id: null,
23
+ job_template_id: 178,
24
+ value: 'default effective user',
25
+ overridable: true,
26
+ current_user: false,
27
+ },
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
+ selectors.selectJobCategories.mockImplementation(() => jobCategories);
97
+ selectors.selectJobTemplates.mockImplementation(() => [
98
+ jobTemplate,
99
+ { ...jobTemplate, id: 2, name: 'template2' },
100
+ ]);
101
+ const mockStore = configureMockStore([]);
102
+ const store = mockStore({});
103
+ return store;
104
+ };
105
+
106
+ export const mockTemplate = selectors => {
107
+ selectors.selectJobTemplate.mockImplementation(() => jobTemplate);
108
+ selectors.selectJobCategoriesStatus.mockImplementation(() => 'RESOLVED');
109
+ };
110
+ export const mockApi = api => {
111
+ api.get.mockImplementation(({ handleSuccess, ...action }) => {
112
+ if (action.key === 'JOB_CATEGORIES') {
113
+ handleSuccess &&
114
+ handleSuccess({ data: { job_categories: jobCategories } });
115
+ } else if (action.key === 'JOB_TEMPLATE') {
116
+ handleSuccess &&
117
+ handleSuccess({
118
+ data: jobTemplateResponse,
119
+ });
120
+ } else if (action.key === 'JOB_TEMPLATES') {
121
+ handleSuccess &&
122
+ handleSuccess({
123
+ data: { results: [jobTemplate] },
124
+ });
125
+ }
126
+ return { type: 'get', ...action };
127
+ });
128
+ };