Skip to content

Commit 1fc6bc6

Browse files
authored
Refs #39167 - add tests for job wizard templates (#1025)
* Refs #39167 - add tests for job wizard templates * Refs #39167 - clean up test job wizard test
1 parent c3cd96e commit 1fc6bc6

1 file changed

Lines changed: 234 additions & 0 deletions

File tree

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# frozen_string_literal: true
2+
3+
require File.expand_path('../test_plugin_helper', __dir__)
4+
require 'integration_test_helper'
5+
require 'securerandom'
6+
7+
class JobWizardCategoryJsTest < IntegrationTestWithJavascript
8+
WIZARD_INPUT_NAME_BASE = 'wizard_integration_input'
9+
WIZARD_FEATURE_INPUT_NAME_BASE = 'wizard_integration_feature_input'
10+
11+
setup do
12+
unique_suffix = SecureRandom.hex(6)
13+
@wizard_input_name = "#{WIZARD_INPUT_NAME_BASE}_#{unique_suffix}"
14+
@wizard_feature_input_name = "#{WIZARD_FEATURE_INPUT_NAME_BASE}_#{unique_suffix}"
15+
default_template_name = "Integration Default REX Form Template #{unique_suffix}"
16+
feature_template_name = "Integration Feature REX Template #{unique_suffix}"
17+
18+
# Prevent real Dynflow proxy calls in CI while planning jobs.
19+
ProxyAPI::ForemanDynflow::DynflowProxy.any_instance.stubs(:tasks_count).returns(0)
20+
21+
@original_rex_form_template = Setting[:remote_execution_form_job_template]
22+
as_admin do
23+
@default_job_template = FactoryBot.create(
24+
:job_template,
25+
:name => default_template_name,
26+
:job_category => 'Default Form Category'
27+
)
28+
@feature_job_template = FactoryBot.create(
29+
:job_template,
30+
:name => feature_template_name,
31+
:job_category => 'Feature Linked Category'
32+
)
33+
add_plain_template_input!(@default_job_template, :name => @wizard_input_name)
34+
add_plain_template_input!(@feature_job_template, :name => @wizard_feature_input_name)
35+
Setting[:remote_execution_form_job_template] = @default_job_template.name
36+
@remote_execution_feature = FactoryBot.create(
37+
:remote_execution_feature,
38+
:label => "integration_rex_category_feature_#{unique_suffix}",
39+
:name => "Integration REX Category Feature #{unique_suffix}",
40+
:job_template => @feature_job_template
41+
)
42+
@rex_host = FactoryBot.create(:host, :with_execution)
43+
end
44+
end
45+
46+
teardown do
47+
Setting[:remote_execution_form_job_template] = @original_rex_form_template
48+
end
49+
50+
test 'job wizard shows feature template job category when feature query param is present' do
51+
visit new_job_invocation_path(:feature => @remote_execution_feature.label)
52+
assert_text 'Category and template', :wait => 30
53+
wait_for_ajax
54+
assert_selector(:ouia_component_id, 'job_category', :text => 'Feature Linked Category', :wait => 30)
55+
end
56+
57+
test 'job wizard shows default form category when no feature query param' do
58+
visit new_job_invocation_path
59+
assert_text 'Category and template', :wait => 30
60+
wait_for_ajax
61+
assert_selector(:ouia_component_id, 'job_category', :text => 'Default Form Category', :wait => 30)
62+
end
63+
64+
test 'job wizard prefills template input from inputs query param' do
65+
visit new_job_invocation_path(
66+
:inputs => { @wizard_input_name => 'from_query_string' },
67+
:search => ''
68+
)
69+
go_to_target_hosts_and_inputs_step!
70+
assert_selector "textarea##{@wizard_input_name}", :wait => 30
71+
assert_equal 'from_query_string', find("textarea##{@wizard_input_name}").value
72+
end
73+
74+
test 'job wizard does not prefill template input when inputs query param is omitted' do
75+
visit new_job_invocation_path(:search => '')
76+
go_to_target_hosts_and_inputs_step!
77+
assert_selector "textarea##{@wizard_input_name}", :wait => 30
78+
assert_equal '', find("textarea##{@wizard_input_name}").value
79+
end
80+
81+
test 'job wizard prefills template input when feature and inputs query params are present' do
82+
visit new_job_invocation_path(
83+
:feature => @remote_execution_feature.label,
84+
:inputs => { @wizard_feature_input_name => 'feature_and_inputs' },
85+
:search => ''
86+
)
87+
go_to_target_hosts_and_inputs_step!
88+
assert_selector "textarea##{@wizard_feature_input_name}", :wait => 30
89+
assert_equal 'feature_and_inputs', find("textarea##{@wizard_feature_input_name}").value
90+
end
91+
92+
test 'job wizard run on selected hosts is enabled on review and posts to job invocations API' do
93+
posted_to_job_invocations_create = false
94+
subscriber = ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |_n, _s, _f, _i, payload|
95+
next unless payload[:controller] == 'Api::V2::JobInvocationsController' && payload[:action].to_s == 'create'
96+
root = payload[:params]
97+
root = root.to_unsafe_h if root.respond_to?(:to_unsafe_h)
98+
job_inv = (root[:job_invocation] || root['job_invocation'])
99+
next unless job_inv
100+
job_inv = job_inv.to_unsafe_h if job_inv.respond_to?(:to_unsafe_h)
101+
next unless job_inv['job_template_id'].to_i == @default_job_template.id
102+
posted_to_job_invocations_create = true
103+
end
104+
105+
begin
106+
visit new_job_invocation_path(:search => "id = #{@rex_host.id}")
107+
navigate_job_wizard_to_review_step!
108+
109+
assert_selector(:ouia_component_id, 'run-on-selected-hosts-footer', :wait => 30)
110+
run_on_hosts = find(:ouia_component_id, 'run-on-selected-hosts-footer')
111+
assert_not_equal 'true', run_on_hosts[:'aria-disabled'], 'expected run-on-selected-hosts button to be enabled'
112+
assert_text 'Run on selected hosts'
113+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
114+
wait_for_ajax
115+
assert posted_to_job_invocations_create, 'expected Api::V2::JobInvocationsController#create to run'
116+
assert_match(%r{\A/job_invocations/\d+\z}, page.current_path)
117+
ensure
118+
ActiveSupport::Notifications.unsubscribe(subscriber)
119+
end
120+
end
121+
122+
test 'job wizard create API request sends job_template_id and inputs for default template' do
123+
input_value = 'api_payload_default_template'
124+
job_inv_params = capture_api_job_invocation_params_during(expected_template_id: @default_job_template.id) do
125+
visit new_job_invocation_path(
126+
:search => "id = #{@rex_host.id}",
127+
:inputs => { @wizard_input_name => input_value }
128+
)
129+
navigate_job_wizard_to_review_step!
130+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
131+
wait_for_ajax
132+
end
133+
134+
assert job_inv_params, 'expected job_invocation key in POST /api/job_invocations'
135+
assert job_inv_params[:job_template_id].present?, 'expected job_template_id to be present'
136+
assert_equal @default_job_template.id, job_inv_params[:job_template_id].to_i
137+
assert job_inv_params[:feature].blank?
138+
assert_equal input_value, hash_from_params_object(job_inv_params[:inputs])[@wizard_input_name]
139+
job_invocation_id = page.current_path.split('/').last.to_i
140+
assert_equal @default_job_template.job_category, JobInvocation.find(job_invocation_id).job_category, 'expected job category to be the default template category'
141+
end
142+
143+
test 'job wizard create API request sends feature label and inputs without job_template_id' do
144+
input_value = 'api_payload_feature_template'
145+
job_inv_params = capture_api_job_invocation_params_during(expected_feature: @remote_execution_feature.label) do
146+
visit new_job_invocation_path(
147+
:feature => @remote_execution_feature.label,
148+
:search => "id = #{@rex_host.id}",
149+
:inputs => { @wizard_feature_input_name => input_value }
150+
)
151+
navigate_job_wizard_to_review_step!
152+
find(:ouia_component_id, 'run-on-selected-hosts-footer').click
153+
wait_for_ajax
154+
end
155+
156+
assert job_inv_params, 'expected job_invocation key in POST /api/job_invocations'
157+
assert_not job_inv_params[:job_template_id].present?
158+
assert_equal @remote_execution_feature.label, job_inv_params[:feature]
159+
assert_equal input_value, hash_from_params_object(job_inv_params[:inputs])[@wizard_feature_input_name]
160+
job_invocation_id = page.current_path.split('/').last.to_i
161+
assert_equal @feature_job_template.job_category, JobInvocation.find(job_invocation_id).job_category, 'expected job category to be the feature template category'
162+
end
163+
164+
private
165+
166+
def capture_api_job_invocation_params_during(expected_template_id: nil, expected_feature: nil)
167+
captured = nil
168+
subscriber = ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |_n, _s, _f, _i, payload|
169+
next unless payload[:controller] == 'Api::V2::JobInvocationsController' && payload[:action].to_s == 'create'
170+
root = payload[:params]
171+
root = root.to_unsafe_h if root.respond_to?(:to_unsafe_h)
172+
job_inv_params = root[:job_invocation] || root['job_invocation']
173+
next unless job_inv_params
174+
job_inv_params = job_inv_params.to_unsafe_h if job_inv_params.respond_to?(:to_unsafe_h)
175+
job_inv_params = job_inv_params.with_indifferent_access
176+
next if expected_template_id && job_inv_params[:job_template_id].to_i != expected_template_id
177+
next if expected_feature && job_inv_params[:feature] != expected_feature
178+
captured = job_inv_params
179+
end
180+
begin
181+
yield
182+
ensure
183+
ActiveSupport::Notifications.unsubscribe(subscriber)
184+
end
185+
captured
186+
end
187+
188+
def hash_from_params_object(obj)
189+
return {}.with_indifferent_access if obj.blank?
190+
h = obj.respond_to?(:to_unsafe_h) ? obj.to_unsafe_h : obj.to_h
191+
h.with_indifferent_access
192+
end
193+
194+
def add_plain_template_input!(job_template, name:)
195+
job_template.template_inputs << FactoryBot.build(
196+
:template_input,
197+
:name => name,
198+
:input_type => 'user',
199+
:value_type => 'plain',
200+
:required => false
201+
)
202+
job_template.save!
203+
end
204+
205+
def job_wizard_click_next!
206+
wait_for_ajax
207+
button = find(:ouia_component_id, 'next-footer')
208+
page.execute_script('arguments[0].scrollIntoView({block: "center"})', button.native)
209+
button.click
210+
wait_for_ajax
211+
end
212+
213+
def go_to_target_hosts_and_inputs_step!
214+
assert_text 'Category and template', :wait => 30
215+
wait_for_ajax
216+
job_wizard_click_next!
217+
assert_text 'Target hosts and inputs', :wait => 30
218+
wait_for_ajax
219+
end
220+
221+
def navigate_job_wizard_to_review_step!
222+
assert_text 'Category and template', :wait => 30
223+
wait_for_ajax
224+
job_wizard_click_next!
225+
assert_text 'Target hosts and inputs', :wait => 30
226+
job_wizard_click_next!
227+
assert_text 'Advanced fields', :wait => 30
228+
job_wizard_click_next!
229+
assert_text 'Immediate execution', :wait => 30
230+
job_wizard_click_next!
231+
assert_text 'Review details', :wait => 30
232+
wait_for_ajax
233+
end
234+
end

0 commit comments

Comments
 (0)