foreman_remote_execution 4.2.2 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/js_ci.yml +29 -0
  3. data/.github/workflows/{ci.yml → ruby_ci.yml} +22 -50
  4. data/.prettierrc +4 -0
  5. data/.rubocop.yml +13 -49
  6. data/.rubocop_todo.yml +326 -102
  7. data/Gemfile +1 -4
  8. data/app/controllers/api/v2/job_invocations_controller.rb +28 -23
  9. data/app/controllers/foreman_remote_execution/concerns/api/v2/registration_commands_controller_extensions.rb +19 -0
  10. data/app/controllers/job_templates_controller.rb +4 -4
  11. data/app/controllers/ui_job_wizard_controller.rb +30 -0
  12. data/app/helpers/job_invocations_helper.rb +2 -2
  13. data/app/helpers/remote_execution_helper.rb +35 -8
  14. data/app/lib/actions/remote_execution/run_host_job.rb +68 -5
  15. data/app/lib/foreman_remote_execution/provider_input.rb +29 -0
  16. data/app/lib/foreman_remote_execution/renderer/scope/input.rb +1 -0
  17. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +5 -5
  18. data/app/models/host_status/execution_status.rb +12 -5
  19. data/app/models/invocation_provider_input_value.rb +12 -0
  20. data/app/models/job_invocation.rb +28 -8
  21. data/app/models/job_invocation_composer.rb +72 -17
  22. data/app/models/remote_execution_provider.rb +17 -2
  23. data/app/models/setting/remote_execution.rb +10 -0
  24. data/app/models/ssh_execution_provider.rb +4 -4
  25. data/app/models/template_invocation.rb +2 -0
  26. data/app/overrides/execution_interface.rb +8 -8
  27. data/app/overrides/subnet_proxies.rb +6 -6
  28. data/app/services/renderer_methods.rb +12 -0
  29. data/app/views/job_invocations/_form.html.erb +8 -0
  30. data/app/views/template_invocations/show.html.erb +30 -23
  31. data/config/routes.rb +5 -0
  32. data/db/migrate/20180110104432_rename_template_invocation_permission.rb +1 -1
  33. data/db/migrate/20190111153330_remove_remote_execution_without_proxy_setting.rb +4 -4
  34. data/db/migrate/20210312074713_add_provider_inputs.rb +10 -0
  35. data/extra/cockpit/foreman-cockpit-session +6 -6
  36. data/foreman_remote_execution.gemspec +1 -2
  37. data/lib/foreman_remote_execution/engine.rb +22 -7
  38. data/lib/foreman_remote_execution/version.rb +1 -1
  39. data/lib/tasks/foreman_remote_execution_tasks.rake +1 -18
  40. data/locale/action_names.rb +1 -0
  41. data/locale/de/foreman_remote_execution.po +77 -27
  42. data/locale/en/foreman_remote_execution.po +77 -27
  43. data/locale/en_GB/foreman_remote_execution.po +77 -27
  44. data/locale/es/foreman_remote_execution.po +77 -27
  45. data/locale/foreman_remote_execution.pot +241 -163
  46. data/locale/fr/foreman_remote_execution.po +77 -27
  47. data/locale/ja/foreman_remote_execution.po +77 -27
  48. data/locale/ko/foreman_remote_execution.po +77 -27
  49. data/locale/pt_BR/foreman_remote_execution.po +77 -27
  50. data/locale/ru/foreman_remote_execution.po +77 -27
  51. data/locale/zh_CN/foreman_remote_execution.po +77 -27
  52. data/locale/zh_TW/foreman_remote_execution.po +77 -27
  53. data/package.json +4 -2
  54. data/test/functional/api/v2/job_invocations_controller_test.rb +38 -5
  55. data/test/functional/api/v2/registration_controller_test.rb +4 -13
  56. data/test/functional/ui_job_wizard_controller_test.rb +16 -0
  57. data/test/helpers/remote_execution_helper_test.rb +16 -0
  58. data/test/unit/job_invocation_composer_test.rb +86 -2
  59. data/test/unit/job_invocation_report_template_test.rb +57 -0
  60. data/webpack/JobWizard/JobWizard.js +96 -0
  61. data/webpack/JobWizard/JobWizard.scss +14 -0
  62. data/webpack/JobWizard/JobWizardConstants.js +6 -0
  63. data/webpack/JobWizard/JobWizardSelectors.js +38 -0
  64. data/webpack/JobWizard/__tests__/JobWizard.test.js +13 -0
  65. data/webpack/JobWizard/__tests__/__snapshots__/JobWizard.test.js.snap +32 -0
  66. data/webpack/JobWizard/__tests__/__snapshots__/integration.test.js.snap +43 -0
  67. data/webpack/JobWizard/__tests__/fixtures.js +26 -0
  68. data/webpack/JobWizard/__tests__/integration.test.js +156 -0
  69. data/webpack/JobWizard/index.js +32 -0
  70. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +93 -0
  71. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +181 -0
  72. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +25 -0
  73. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +249 -0
  74. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +109 -0
  75. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +52 -0
  76. data/webpack/JobWizard/steps/CategoryAndTemplate/__snapshots__/CategoryAndTemplate.test.js.snap +113 -0
  77. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +94 -0
  78. data/webpack/JobWizard/steps/form/FormHelpers.js +19 -0
  79. data/webpack/JobWizard/steps/form/GroupedSelectField.js +91 -0
  80. data/webpack/JobWizard/steps/form/SelectField.js +48 -0
  81. data/webpack/JobWizard/steps/form/__tests__/GroupedSelectField.test.js +38 -0
  82. data/webpack/JobWizard/steps/form/__tests__/SelectField.test.js +23 -0
  83. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/GroupedSelectField.test.js.snap +37 -0
  84. data/webpack/JobWizard/steps/form/__tests__/__snapshots__/SelectField.test.js.snap +23 -0
  85. data/webpack/Routes/routes.js +12 -0
  86. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  87. data/webpack/__mocks__/foremanReact/history.js +1 -0
  88. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +21 -2
  89. data/webpack/__mocks__/foremanReact/redux/API/index.js +5 -0
  90. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +10 -0
  91. data/webpack/global_index.js +10 -0
  92. data/webpack/index.js +3 -4
  93. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +83 -0
  94. data/webpack/react_app/components/RecentJobsCard/constants.js +1 -0
  95. data/webpack/react_app/components/RecentJobsCard/index.js +1 -0
  96. data/webpack/react_app/components/RecentJobsCard/styles.css +15 -0
  97. data/webpack/react_app/components/RegistrationExtension/RexInterface.js +50 -0
  98. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +9 -0
  99. data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +35 -0
  100. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.js +1 -1
  101. data/webpack/react_app/components/TargetingHosts/TargetingHostsPage.scss +0 -3
  102. data/webpack/react_app/components/TargetingHosts/__tests__/TargetingHostsSelectors.test.js +8 -3
  103. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsPage.test.js.snap +1 -1
  104. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/TargetingHostsSelectors.test.js.snap +7 -2
  105. data/webpack/react_app/extend/fillRecentJobsCard.js +11 -0
  106. data/webpack/react_app/extend/fillregistrationAdvanced.js +11 -0
  107. data/webpack/react_app/extend/reducers.js +5 -0
  108. metadata +58 -20
  109. data/app/views/api/v2/registration/_form.html.erb +0 -12
@@ -16,16 +16,34 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
16
16
  setup_user('create', 'hosts')
17
17
  end
18
18
 
19
+ class AnsibleInputs < RemoteExecutionProvider
20
+ class << self
21
+ def provider_input_namespace
22
+ :ansible
23
+ end
24
+
25
+ def provider_inputs
26
+ [
27
+ ForemanRemoteExecution::ProviderInput.new(name: 'tags', label: 'Tags', value: 'fooo', value_type: 'plain'),
28
+ ForemanRemoteExecution::ProviderInput.new(name: 'tags_flag', label: 'Tags Flag', value: '--tags', options: "--tags\n--skip-tags"),
29
+ ]
30
+ end
31
+ end
32
+ end
33
+ RemoteExecutionProvider.register(:AnsibleInputs, AnsibleInputs)
34
+
19
35
  let(:trying_job_template_1) { FactoryBot.create(:job_template, :job_category => 'trying_job_template_1', :name => 'trying1', :provider_type => 'SSH') }
20
36
  let(:trying_job_template_2) { FactoryBot.create(:job_template, :job_category => 'trying_job_template_2', :name => 'trying2', :provider_type => 'Mcollective') }
21
37
  let(:trying_job_template_3) { FactoryBot.create(:job_template, :job_category => 'trying_job_template_1', :name => 'trying3', :provider_type => 'SSH') }
22
38
  let(:unauthorized_job_template_1) { FactoryBot.create(:job_template, :job_category => 'trying_job_template_1', :name => 'unauth1', :provider_type => 'SSH') }
23
39
  let(:unauthorized_job_template_2) { FactoryBot.create(:job_template, :job_category => 'unauthorized_job_template_2', :name => 'unauth2', :provider_type => 'Ansible') }
24
40
 
41
+ let(:provider_inputs_job_template) { FactoryBot.create(:job_template, :job_category => 'trying_test_inputs', :name => 'trying provider inputs', :provider_type => 'AnsibleInputs') }
25
42
 
26
43
  let(:input1) { FactoryBot.create(:template_input, :template => trying_job_template_1, :input_type => 'user') }
27
44
  let(:input2) { FactoryBot.create(:template_input, :template => trying_job_template_3, :input_type => 'user') }
28
45
  let(:input3) { FactoryBot.create(:template_input, :template => trying_job_template_1, :input_type => 'user', :required => true) }
46
+ let(:input4) { FactoryBot.create(:template_input, :template => provider_inputs_job_template, :input_type => 'user') }
29
47
  let(:unauthorized_input1) { FactoryBot.create(:template_input, :template => unauthorized_job_template_1, :input_type => 'user') }
30
48
 
31
49
  let(:ansible_params) { { } }
@@ -604,8 +622,10 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
604
622
  end
605
623
  end
606
624
 
607
- describe '#from_api_params' do
608
- let(:composer) { JobInvocationComposer.from_api_params(params) }
625
+ describe '.from_api_params' do
626
+ let(:composer) do
627
+ JobInvocationComposer.from_api_params(params)
628
+ end
609
629
  let(:bookmark) { bookmarks(:one) }
610
630
 
611
631
  context 'with targeting from bookmark' do
@@ -659,6 +679,26 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
659
679
  end
660
680
  end
661
681
 
682
+ context 'with provider inputs' do
683
+ let(:params) do
684
+ { :job_category => provider_inputs_job_template.job_category,
685
+ :job_template_id => provider_inputs_job_template.id,
686
+ :targeting_type => 'static_query',
687
+ :search_query => 'some hosts',
688
+ :inputs => { input4.name => 'some_value' },
689
+ :ansible => { 'tags' => 'bar', 'tags_flag' => '--skip-tags' } }
690
+ end
691
+
692
+ it 'detects provider inputs' do
693
+ assert composer.save!
694
+ scope = composer.job_invocation.pattern_template_invocations.first.provider_input_values
695
+ tags = scope.find_by :name => 'tags'
696
+ flags = scope.find_by :name => 'tags_flag'
697
+ assert_equal 'bar', tags.value
698
+ assert_equal '--skip-tags', flags.value
699
+ end
700
+ end
701
+
662
702
  context 'with effective user' do
663
703
  let(:params) do
664
704
  { :job_category => trying_job_template_1.job_category,
@@ -714,12 +754,23 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
714
754
  assert composer.save!
715
755
  _(composer.job_invocation.remote_execution_feature).must_equal feature
716
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
717
767
  end
718
768
 
719
769
  context 'with invalid targeting' do
720
770
  let(:params) do
721
771
  { :job_category => trying_job_template_1.job_category,
722
772
  :job_template_id => trying_job_template_1.id,
773
+ :targeting_type => 'fake',
723
774
  :search_query => 'some hosts',
724
775
  :inputs => {input1.name => 'some_value'}}
725
776
  end
@@ -825,6 +876,39 @@ class JobInvocationComposerTest < ActiveSupport::TestCase
825
876
  end
826
877
  end
827
878
 
879
+ describe '.for_feature' do
880
+ let(:feature) { FactoryBot.create(:remote_execution_feature, job_template: trying_job_template_1) }
881
+ let(:host) { FactoryBot.create(:host) }
882
+ let(:bookmark) { Bookmark.create!(:query => 'b', :name => 'bookmark', :public => true, :controller => 'hosts') }
883
+
884
+ context 'specifying hosts' do
885
+ it 'takes a bookmarked search' do
886
+ composer = JobInvocationComposer.for_feature(feature.label, bookmark, {})
887
+ assert_equal bookmark.id, composer.params['targeting']['bookmark_id']
888
+ end
889
+
890
+ it 'takes an array of host ids' do
891
+ composer = JobInvocationComposer.for_feature(feature.label, [host.id], {})
892
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
893
+ end
894
+
895
+ it 'takes a single host object' do
896
+ composer = JobInvocationComposer.for_feature(feature.label, host, {})
897
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
898
+ end
899
+
900
+ it 'takes an array of host FQDNs' do
901
+ composer = JobInvocationComposer.for_feature(feature.label, [host.fqdn], {})
902
+ assert_match(/#{host.name}/, composer.params['targeting']['search_query'])
903
+ end
904
+
905
+ it 'takes a search query string' do
906
+ composer = JobInvocationComposer.for_feature(feature.label, 'host.example.com', {})
907
+ assert_equal 'host.example.com', composer.search_query
908
+ end
909
+ end
910
+ end
911
+
828
912
  describe '#resolve_job_category and #resolve job_templates' do
829
913
  let(:setting_template) { as_admin { FactoryBot.create(:job_template, :name => 'trying setting', :job_category => 'fluff') } }
830
914
  let(:other_template) { as_admin { FactoryBot.create(:job_template, :name => 'trying something', :job_category => 'fluff') } }
@@ -0,0 +1,57 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class JobReportTemplateTest < ActiveSupport::TestCase
4
+ class FakeTask < OpenStruct
5
+ class Jail < Safemode::Jail
6
+ allow :action_continuous_output, :result, :ended_at
7
+ end
8
+ end
9
+
10
+ context 'with valid job invocation report template' do
11
+ let(:job_invocation_template) do
12
+ file_path = File.read(File.expand_path(Rails.root + "app/views/unattended/report_templates/jobs_-_invocation_report_template.erb"))
13
+ template = ReportTemplate.import_without_save("Job Invocation Report Template", file_path)
14
+ template.save!
15
+ template
16
+ end
17
+
18
+ describe 'template setting' do
19
+ it 'in settings includes only report templates with job_id input' do
20
+ FactoryBot.create(:report_template, name: 'Template 1')
21
+ job_invocation_template
22
+ templates = Setting::RemoteExecution.job_invocation_report_templates_select
23
+
24
+ assert_include templates, 'Job Invocation Report Template'
25
+ end
26
+ end
27
+
28
+ describe 'task reporting' do
29
+ let(:fake_outputs) do
30
+ [
31
+ { 'output_type' => 'stderr', 'output' => "error", 'timestamp' => Time.new(2020, 12, 1, 0, 0, 0).utc },
32
+ { 'output_type' => 'stdout', 'output' => "output", 'timestamp' => Time.new(2020, 12, 1, 0, 0, 0).utc },
33
+ { 'output_type' => 'stdebug', 'output' => "debug", 'timestamp' => Time.new(2020, 12, 1, 0, 0, 0).utc },
34
+ ]
35
+ end
36
+ let(:fake_task) { FakeTask.new(result: 'success', action_continuous_output: fake_outputs) }
37
+
38
+ it 'should render task outputs' do
39
+ job_invocation = FactoryBot.create(:job_invocation, :with_task)
40
+ JobInvocation.any_instance.expects(:sub_task_for_host).returns(fake_task)
41
+
42
+ input = job_invocation_template.template_inputs.first
43
+ composer_params = { template_id: job_invocation_template.id, input_values: { input.id.to_s => { value: job_invocation.id.to_s } } }
44
+ result = ReportComposer.new(composer_params).render
45
+
46
+ # parsing the CSV result
47
+ CSV.parse(result.strip, headers: true).each_with_index do |row, i|
48
+ row_hash = row.to_h
49
+ assert_equal 'success', row_hash['result']
50
+ assert_equal fake_outputs[i]['output_type'], row_hash['type']
51
+ assert_equal fake_outputs[i]['output'], row_hash['message']
52
+ assert_kind_of Time, Time.zone.parse(row_hash['time']), 'Parsing of time column failed'
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,96 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import { Wizard } from '@patternfly/react-core';
4
+ import { get } from 'foremanReact/redux/API';
5
+ import { translate as __ } from 'foremanReact/common/I18n';
6
+ import history from 'foremanReact/history';
7
+ import CategoryAndTemplate from './steps/CategoryAndTemplate/';
8
+ import { AdvancedFields } from './steps/AdvancedFields/AdvancedFields';
9
+ import { JOB_TEMPLATE } from './JobWizardConstants';
10
+ import { selectTemplateError } from './JobWizardSelectors';
11
+ import './JobWizard.scss';
12
+
13
+ export const JobWizard = () => {
14
+ const [jobTemplateID, setJobTemplateID] = useState(null);
15
+ const [category, setCategory] = useState('');
16
+ const [advancedValues, setAdvancedValues] = useState({});
17
+ const dispatch = useDispatch();
18
+
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
+ }, []);
26
+ useEffect(() => {
27
+ if (jobTemplateID) {
28
+ dispatch(
29
+ get({
30
+ key: JOB_TEMPLATE,
31
+ url: `/ui_job_wizard/template/${jobTemplateID}`,
32
+ handleSuccess: setDefaults,
33
+ })
34
+ );
35
+ }
36
+ }, [jobTemplateID, setDefaults, dispatch]);
37
+
38
+ const templateError = !!useSelector(selectTemplateError);
39
+ const isTemplate = !templateError && !!jobTemplateID;
40
+ const steps = [
41
+ {
42
+ name: __('Category and Template'),
43
+ component: (
44
+ <CategoryAndTemplate
45
+ jobTemplate={jobTemplateID}
46
+ setJobTemplate={setJobTemplateID}
47
+ category={category}
48
+ setCategory={setCategory}
49
+ />
50
+ ),
51
+ },
52
+ {
53
+ name: __('Target Hosts'),
54
+ component: <p>Target Hosts</p>,
55
+ canJumpTo: isTemplate,
56
+ },
57
+ {
58
+ name: __('Advanced Fields'),
59
+ component: (
60
+ <AdvancedFields
61
+ advancedValues={advancedValues}
62
+ setAdvancedValues={newValues => {
63
+ setAdvancedValues(currentAdvancedValues => ({
64
+ ...currentAdvancedValues,
65
+ ...newValues,
66
+ }));
67
+ }}
68
+ jobTemplateID={jobTemplateID}
69
+ />
70
+ ),
71
+ canJumpTo: isTemplate,
72
+ },
73
+ {
74
+ name: __('Schedule'),
75
+ component: <p>Schedule</p>,
76
+ canJumpTo: isTemplate,
77
+ },
78
+ {
79
+ name: __('Review Details'),
80
+ component: <p>Review Details</p>,
81
+ nextButtonText: 'Run',
82
+ canJumpTo: isTemplate,
83
+ },
84
+ ];
85
+ return (
86
+ <Wizard
87
+ onClose={() => history.goBack()}
88
+ navAriaLabel="Run Job steps"
89
+ steps={steps}
90
+ height="100%"
91
+ className="job-wizard"
92
+ />
93
+ );
94
+ };
95
+
96
+ export default JobWizard;
@@ -0,0 +1,14 @@
1
+ .job-wizard {
2
+ .pf-c-wizard__main {
3
+ z-index: calc(
4
+ var(--pf-c-wizard__footer--ZIndex) + 1
5
+ ); // So the select box can be shown above the wizard footer
6
+ }
7
+
8
+ .pf-c-wizard__main-body {
9
+ max-width: 500px;
10
+ .advanced-fields-title {
11
+ margin-bottom: 10px;
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,6 @@
1
+ import { foremanUrl } from 'foremanReact/common/helpers';
2
+
3
+ export const JOB_TEMPLATES = 'JOB_TEMPLATES';
4
+ export const JOB_CATEGORIES = 'JOB_CATEGORIES';
5
+ export const JOB_TEMPLATE = 'JOB_TEMPLATE';
6
+ export const templatesUrl = foremanUrl('/api/v2/job_templates');
@@ -0,0 +1,38 @@
1
+ import {
2
+ selectAPIResponse,
3
+ selectAPIStatus,
4
+ selectAPIErrorMessage,
5
+ } from 'foremanReact/redux/API/APISelectors';
6
+
7
+ import {
8
+ JOB_TEMPLATES,
9
+ JOB_CATEGORIES,
10
+ JOB_TEMPLATE,
11
+ } from './JobWizardConstants';
12
+
13
+ export const selectJobTemplatesStatus = state =>
14
+ selectAPIStatus(state, JOB_TEMPLATES);
15
+
16
+ export const filterJobTemplates = templates =>
17
+ templates?.filter(template => !template.snippet) || [];
18
+
19
+ export const selectJobTemplates = state =>
20
+ filterJobTemplates(selectAPIResponse(state, JOB_TEMPLATES)?.results);
21
+
22
+ export const selectJobCategories = state =>
23
+ selectAPIResponse(state, JOB_CATEGORIES).job_categories || [];
24
+
25
+ export const selectJobCategoriesStatus = state =>
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);
@@ -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
+ `;
@@ -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
+ `;