foreman_remote_execution 4.4.0 → 4.5.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ };