foreman_openbolt 1.1.0 → 1.1.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -15
  3. data/Rakefile +0 -0
  4. data/lib/foreman_openbolt/version.rb +1 -1
  5. data/package.json +1 -1
  6. data/test/acceptance/acceptance_helper.rb +146 -0
  7. data/test/acceptance/docker/docker-compose.yml +69 -0
  8. data/test/acceptance/docker/foreman/Dockerfile +45 -0
  9. data/test/acceptance/docker/foreman/entrypoint.sh +26 -0
  10. data/test/acceptance/docker/target/Dockerfile +29 -0
  11. data/test/acceptance/docker/target/entrypoint.sh +11 -0
  12. data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.json +30 -0
  13. data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.sh +16 -0
  14. data/test/acceptance/fixtures/modules/acceptance/tasks/echo.json +13 -0
  15. data/test/acceptance/fixtures/modules/acceptance/tasks/echo.sh +3 -0
  16. data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.json +8 -0
  17. data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.sh +3 -0
  18. data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.json +8 -0
  19. data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.sh +2 -0
  20. data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.json +14 -0
  21. data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.sh +3 -0
  22. data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.json +13 -0
  23. data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.sh +9 -0
  24. data/test/acceptance/fixtures/openbolt.yml +7 -0
  25. data/test/acceptance/tests/error_handling_test.rb +40 -0
  26. data/test/acceptance/tests/host_selector_test.rb +31 -0
  27. data/test/acceptance/tests/launch_task_test.rb +96 -0
  28. data/test/acceptance/tests/parameter_table_test.rb +61 -0
  29. data/test/acceptance/tests/settings_test.rb +95 -0
  30. data/test/acceptance/tests/ssh_options_test.rb +77 -0
  31. data/test/acceptance/tests/task_execution_test.rb +40 -0
  32. data/test/acceptance/tests/task_history_test.rb +84 -0
  33. data/test/acceptance/tests/transport_options_test.rb +121 -0
  34. data/test/test_plugin_helper.rb +17 -0
  35. data/test/unit/controllers/task_controller_test.rb +351 -0
  36. data/test/unit/docker/Dockerfile +47 -0
  37. data/test/unit/docker/docker-compose.yml +33 -0
  38. data/test/unit/docker/entrypoint.sh +4 -0
  39. data/test/unit/factories/foreman_openbolt_factories.rb +39 -0
  40. data/test/unit/lib/actions/cleanup_proxy_artifacts_test.rb +51 -0
  41. data/test/unit/lib/actions/poll_task_status_test.rb +141 -0
  42. data/test/unit/lib/proxy_api/openbolt_test.rb +174 -0
  43. data/test/unit/models/task_job_test.rb +278 -0
  44. metadata +39 -1
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests launching various task types and verifying their execution results.
6
+ class LaunchTaskTest < AcceptanceTestCase
7
+ def setup
8
+ super
9
+ foreman_login
10
+ end
11
+
12
+ def test_echo_task_succeeds_on_all_targets
13
+ launch_task_via_ui('acceptance::echo',
14
+ params: { 'message' => 'hello from acceptance test' })
15
+
16
+ assert_task_completed
17
+ assert_result_has_content
18
+ assert_result_contains 'hello from acceptance test'
19
+ assert_result_contains 'hostname'
20
+ end
21
+
22
+ def test_noop_task_succeeds
23
+ launch_task_via_ui('acceptance::noop_task')
24
+ assert_task_completed
25
+ assert_result_has_content
26
+ end
27
+
28
+ def test_complex_params_task_succeeds
29
+ launch_task_via_ui('acceptance::complex_params',
30
+ params: {
31
+ 'required_string' => 'test_value',
32
+ 'array_param' => '["a","b","c"]',
33
+ 'with_default' => 'overridden',
34
+ })
35
+
36
+ assert_task_completed
37
+ assert_result_has_content
38
+ assert_result_contains 'test_value'
39
+ assert_result_contains 'overridden'
40
+ end
41
+
42
+ def test_slow_task_transitions_through_running
43
+ launch_task_via_ui('acceptance::slow_task', params: { 'seconds' => '8' })
44
+ assert_selector '.pf-v5-c-label', text: /Running/i, wait: 30
45
+ assert_task_completed
46
+ assert_result_has_content
47
+ end
48
+
49
+ def test_failing_task_shows_failure_with_error_detail
50
+ launch_task_via_ui('acceptance::failing_task')
51
+ assert_task_failed
52
+ assert_result_contains 'This task always fails'
53
+ end
54
+
55
+ def test_run_another_task_navigates_back
56
+ launch_task_via_ui('acceptance::noop_task')
57
+ assert_task_completed
58
+ click_button 'Run Another Task'
59
+ assert_selector 'h1', text: 'Launch OpenBolt Task', wait: 15
60
+ end
61
+
62
+ def test_launch_button_disabled_until_all_selections_made
63
+ visit '/foreman_openbolt/page_launch_task'
64
+ assert_selector '#smart-proxy-input', wait: 15
65
+
66
+ # No selections at all
67
+ assert find('button', text: /Launch Task/).disabled?,
68
+ 'Expected Launch Task disabled with no selections'
69
+
70
+ # Proxy only
71
+ select_first_proxy
72
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
73
+ assert find('button', text: /Launch Task/).disabled?,
74
+ 'Expected Launch Task disabled with only proxy selected'
75
+
76
+ # Proxy + task, no targets
77
+ select 'acceptance::noop_task', from: 'task-name-input'
78
+ assert find('button', text: /Launch Task/).disabled?,
79
+ 'Expected Launch Task disabled with no targets'
80
+
81
+ # Proxy + task + targets — button enables
82
+ select_hosts_via_search('target1')
83
+ assert_selector 'button:not([disabled])', text: /Launch Task/, wait: 10
84
+ end
85
+
86
+ def test_running_task_shows_loading_indicator
87
+ launch_task_via_ui('acceptance::slow_task', params: { 'seconds' => '8' })
88
+ # While the job is still polling, LoadingIndicator renders an EmptyState
89
+ # with role=status and a title of "Task is <status>..." (running or pending).
90
+ assert_selector '[role="status"]',
91
+ text: /Task is (running|pending)/i, wait: 30
92
+ assert_selector '.pf-v5-c-empty-state__body',
93
+ text: /update automatically when the task completes/, wait: 5
94
+ assert_task_completed
95
+ end
96
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests the parameter table rendering logic: required indicator
6
+ # based on the Optional[...] type prefix, and the expandable row
7
+ # that reveals type and description for each parameter.
8
+ class ParameterTableTest < AcceptanceTestCase
9
+ def setup
10
+ super
11
+ foreman_login
12
+ visit '/foreman_openbolt/page_launch_task'
13
+ assert_selector '#smart-proxy-input', wait: 15
14
+ select_first_proxy
15
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
16
+ select 'acceptance::complex_params', from: 'task-name-input'
17
+ assert_selector '#param_required_string', wait: 10
18
+ end
19
+
20
+ def test_required_param_shows_required_indicator
21
+ # ParametersSection flags params whose type does not start with
22
+ # "optional" as required, and FieldTable renders a span with
23
+ # role=img and aria-label="Required" in those rows.
24
+ within(:xpath, "//tr[.//input[@id='param_required_string']]") do
25
+ assert_selector 'span[role="img"][aria-label="Required"]'
26
+ end
27
+ end
28
+
29
+ def test_optional_param_has_no_required_indicator
30
+ within(:xpath, "//tr[.//input[@id='param_optional_string']]") do
31
+ assert_no_selector 'span[role="img"][aria-label="Required"]'
32
+ end
33
+ end
34
+
35
+ def test_expanding_required_param_row_shows_type_description_and_required_warning
36
+ within(:xpath, "//tr[.//input[@id='param_required_string']]") do
37
+ first('button').click
38
+ end
39
+
40
+ # The expanded row is a sibling in the same Tbody. Assertions on
41
+ # the page level since the ExpandableRowContent is outside the
42
+ # trigger row's scope.
43
+ assert_selector '.pf-v5-c-helper-text__item',
44
+ text: 'This field is required', wait: 5
45
+ assert_selector '.pf-v5-c-helper-text__item code', text: 'String'
46
+ assert_selector '.pf-v5-c-helper-text__item',
47
+ text: 'A required string parameter'
48
+ end
49
+
50
+ def test_expanding_optional_param_row_shows_type_and_description_without_required_warning
51
+ within(:xpath, "//tr[.//input[@id='param_optional_string']]") do
52
+ first('button').click
53
+ end
54
+
55
+ assert_selector '.pf-v5-c-helper-text__item code', text: 'Optional[String]'
56
+ assert_selector '.pf-v5-c-helper-text__item',
57
+ text: 'An optional string parameter'
58
+ assert_no_selector '.pf-v5-c-helper-text__item',
59
+ text: 'This field is required'
60
+ end
61
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests that Foreman settings correctly populate the OpenBolt options UI
6
+ # and that changes to settings are reflected in the plugin's behavior.
7
+ # This exercises the settings lookup bug fix (Foreman.settings vs Setting.where).
8
+ class SettingsTest < AcceptanceTestCase
9
+ def setup
10
+ super
11
+ foreman_login
12
+ end
13
+
14
+ def test_openbolt_user_setting_populates_default
15
+ visit '/foreman_openbolt/page_launch_task'
16
+ assert_selector '#smart-proxy-input', wait: 15
17
+ select_first_proxy
18
+
19
+ # The user option should show the value configured during acceptance:up
20
+ assert_selector '#param_user', wait: 10
21
+ assert_equal 'openbolt', find_by_id('param_user').value
22
+ end
23
+
24
+ # The string the UI shows in an encrypted-default field so the user can tell
25
+ # a saved value exists without exposing it to the browser. Kept in sync with
26
+ # ENCRYPTED_PLACEHOLDER in task_controller.rb and ENCRYPTED_DEFAULT_PLACEHOLDER
27
+ # in webpack/src/Components/common/constants.js.
28
+ ENCRYPTED_DEFAULT_PLACEHOLDER = '[Use saved encrypted default]'
29
+
30
+ def test_encrypted_setting_shows_placeholder_and_is_overrideable
31
+ # Scope the saved password to this test so it does not leak into other
32
+ # task launches (merge_encrypted_defaults would inject it into bolt).
33
+ update_foreman_setting('openbolt_password', 'acceptance-test-password')
34
+ begin
35
+ visit '/foreman_openbolt/page_launch_task'
36
+ assert_selector '#smart-proxy-input', wait: 15
37
+ select_first_proxy
38
+
39
+ # With a saved encrypted setting, the field prefills with the placeholder
40
+ # string so the user knows the saved value will be used. The real value
41
+ # is never sent to the browser.
42
+ field = find_by_id('param_password', wait: 10)
43
+ assert_equal 'password', field['type']
44
+ assert_equal ENCRYPTED_DEFAULT_PLACEHOLDER, field.value.to_s
45
+
46
+ # Expanding the row reveals a warning that an encrypted default is saved.
47
+ # The FieldTable renders an expand toggle in the first cell of each row;
48
+ # click the first button within the row containing our field.
49
+ within(:xpath, "//tr[.//input[@id='param_password']]") do
50
+ first('button').click
51
+ end
52
+ assert_selector '.pf-v5-c-helper-text__item',
53
+ text: /saved, encrypted default/i, wait: 10
54
+
55
+ # Typing a new value must replace the placeholder so the task uses
56
+ # what the user entered instead of the saved default.
57
+ field.fill_in with: 'override-value'
58
+ assert_equal 'override-value', field.value.to_s
59
+ ensure
60
+ update_foreman_setting('openbolt_password', '')
61
+ end
62
+ end
63
+
64
+ def test_changing_user_setting_updates_launch_page
65
+ # Change the setting through the Foreman settings UI
66
+ update_foreman_setting('openbolt_user', 'newuser')
67
+
68
+ # Verify the launch page reflects the change
69
+ visit '/foreman_openbolt/page_launch_task'
70
+ assert_selector '#smart-proxy-input', wait: 15
71
+ select_first_proxy
72
+ assert_selector '#param_user', wait: 10
73
+ assert_equal 'newuser', find_by_id('param_user').value
74
+
75
+ # Restore the original value
76
+ update_foreman_setting('openbolt_user', 'openbolt')
77
+ end
78
+
79
+ def test_host_key_check_setting_flows_through_to_checkbox_state
80
+ visit '/foreman_openbolt/page_launch_task'
81
+ assert_selector '#smart-proxy-input', wait: 15
82
+ select_first_proxy
83
+
84
+ # openbolt_host-key-check is a boolean Foreman setting; the
85
+ # acceptance fixture (rakelib/acceptance.rake) sets it to false so
86
+ # SSH does not reject the ephemeral target containers. The registered
87
+ # default in engine.rb is true, so if the Foreman setting lookup
88
+ # were broken, the checkbox would render checked. Asserting
89
+ # unchecked here proves the configured value (false) flowed through.
90
+ field = find_by_id('param_host-key-check', wait: 10)
91
+ assert_equal 'checkbox', field['type']
92
+ refute field.checked?,
93
+ 'Expected host-key-check to be unchecked, matching the acceptance fixture value'
94
+ end
95
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests that SSH transport options are correctly passed through to the
6
+ # OpenBolt CLI and affect task execution.
7
+ class SshOptionsTest < AcceptanceTestCase
8
+ def setup
9
+ super
10
+ foreman_login
11
+ visit '/foreman_openbolt/page_launch_task'
12
+ assert_selector '#smart-proxy-input', wait: 15
13
+ select_first_proxy
14
+ end
15
+
16
+ def test_default_user_passed_to_bolt
17
+ select_hosts_via_search('target1')
18
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
19
+ select 'acceptance::echo', from: 'task-name-input'
20
+ fill_in 'param_message', with: 'user test'
21
+ click_button 'Launch Task'
22
+ assert_selector 'h1', text: 'Task Execution', wait: 15
23
+
24
+ assert_task_completed
25
+ # The default user (from settings) should appear in the bolt command on the Log Output tab
26
+ assert_log_contains '--user=openbolt'
27
+ end
28
+
29
+ def test_user_override_passed_to_bolt
30
+ select_hosts_via_search('target1')
31
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
32
+ select 'acceptance::echo', from: 'task-name-input'
33
+ fill_in 'param_message', with: 'override test'
34
+
35
+ set_openbolt_option('user', 'root')
36
+ click_button 'Launch Task'
37
+ assert_selector 'h1', text: 'Task Execution', wait: 15
38
+
39
+ # Task may fail because root doesn't have the SSH key, but the
40
+ # command should show the overridden user
41
+ assert_selector '.pf-v5-c-label', text: /Success|Failed/i, wait: 120
42
+ assert_log_contains '--user=root'
43
+ end
44
+
45
+ def test_host_key_check_false_passed_to_bolt
46
+ select_hosts_via_search('target1')
47
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
48
+ select 'acceptance::noop_task', from: 'task-name-input'
49
+
50
+ # host-key-check is a boolean OpenBolt option and must render as a
51
+ # checkbox (ParameterField's boolean branch), not a text input.
52
+ assert_equal 'checkbox', find_by_id('param_host-key-check')['type']
53
+
54
+ set_openbolt_option('host-key-check', false)
55
+ click_button 'Launch Task'
56
+ assert_selector 'h1', text: 'Task Execution', wait: 15
57
+
58
+ assert_task_completed
59
+ assert_log_contains '--no-host-key-check'
60
+ end
61
+
62
+ def test_verbose_flag_passed_to_bolt
63
+ select_hosts_via_search('target1')
64
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
65
+ select 'acceptance::noop_task', from: 'task-name-input'
66
+
67
+ # verbose is boolean and must render as a checkbox.
68
+ assert_equal 'checkbox', find_by_id('param_verbose')['type']
69
+
70
+ set_openbolt_option('verbose', true)
71
+ click_button 'Launch Task'
72
+ assert_selector 'h1', text: 'Task Execution', wait: 15
73
+
74
+ assert_task_completed
75
+ assert_log_contains '--verbose'
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests for the Task Execution page: URL validation, tab switching, and
6
+ # task metadata display on the Task Details tab.
7
+ class TaskExecutionTest < AcceptanceTestCase
8
+ def setup
9
+ super
10
+ foreman_login
11
+ end
12
+
13
+ def test_execution_page_without_job_id_redirects_to_launch
14
+ # Visiting the execution URL without a job_id query parameter should
15
+ # client-side redirect back to the Launch page (TaskExecution/index.js
16
+ # useEffect with !jobId calls history.push(LAUNCH_TASK)).
17
+ visit '/foreman_openbolt/page_task_execution'
18
+ assert_selector 'h1', text: 'Launch OpenBolt Task', wait: 15
19
+ end
20
+
21
+ def test_task_details_tab_shows_task_name_and_submitted_parameters
22
+ launch_task_via_ui('acceptance::echo',
23
+ params: { 'message' => 'details tab test' })
24
+ assert_task_completed
25
+
26
+ # Click the "Task Details" tab (second tab in ExecutionDisplay).
27
+ find('.pf-v5-c-tabs__link', text: 'Task Details').click
28
+
29
+ # TaskDetails renders task name in a DescriptionList and the
30
+ # submitted parameters in a table with aria-label="Task parameters".
31
+ assert_selector 'dt', text: 'Task Name', wait: 10
32
+ assert_selector 'dd', text: 'acceptance::echo'
33
+
34
+ assert_selector 'table[aria-label="Task parameters"]', wait: 10
35
+ within 'table[aria-label="Task parameters"]' do
36
+ assert_selector 'td', text: 'message'
37
+ assert_selector 'td', text: 'details tab test'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests the task history page: listing, navigation, and new entry creation.
6
+ class TaskHistoryTest < AcceptanceTestCase
7
+ def setup
8
+ super
9
+ foreman_login
10
+ end
11
+
12
+ def test_history_shows_completed_tasks_and_navigation
13
+ # Launch a task to seed history
14
+ launch_task_via_ui('acceptance::echo', params: { 'message' => 'history test' })
15
+ assert_task_completed
16
+
17
+ # Visit history and verify the entry appears
18
+ visit '/foreman_openbolt/page_task_history'
19
+ assert_selector 'h1', text: 'Task History', wait: 15
20
+ assert_no_selector '[aria-label="Loading task history"]', wait: 15
21
+ assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1
22
+ # Capture the first row so we can confirm a newer entry lands above it.
23
+ # History paginates (default 20 rows), so counting rows is unreliable
24
+ # once history fills the first page.
25
+ first_row_before = first('table[aria-label="Task history table"] tbody tr').text
26
+
27
+ # Navigate to execution details from history
28
+ first('a[aria-label="View Details"]').click
29
+ assert_selector 'h1', text: 'Task Execution', wait: 15
30
+
31
+ # Launch another task and verify it appears at the top of history.
32
+ launch_task_via_ui('acceptance::noop_task')
33
+ assert_task_completed
34
+
35
+ visit '/foreman_openbolt/page_task_history'
36
+ assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
37
+ first_row_after = first('table[aria-label="Task history table"] tbody tr').text
38
+ assert_not_equal first_row_before, first_row_after,
39
+ 'Expected newly launched task to appear as the first history row'
40
+ end
41
+
42
+ def test_hosts_popover_lists_targets_from_history_row
43
+ # 'target' matches both target1 and target2, so the popover count
44
+ # will be 2.
45
+ launch_task_via_ui('acceptance::noop_task')
46
+ assert_task_completed
47
+
48
+ visit '/foreman_openbolt/page_task_history'
49
+ assert_no_selector '[aria-label="Loading task history"]', wait: 15
50
+ assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
51
+
52
+ # The hosts count button has aria-label '<count> target hosts'
53
+ within first('table[aria-label="Task history table"] tbody tr') do
54
+ find('button[aria-label$=" target hosts"]').click
55
+ end
56
+
57
+ # Popover content is portaled to document body
58
+ assert_selector 'table[aria-label="Target hosts"]', wait: 10
59
+ within 'table[aria-label="Target hosts"]' do
60
+ assert_selector 'td', text: 'target1'
61
+ assert_selector 'td', text: 'target2'
62
+ end
63
+ end
64
+
65
+ def test_task_popover_shows_submitted_parameters_on_history_row
66
+ launch_task_via_ui('acceptance::echo',
67
+ params: { 'message' => 'popover history param' })
68
+ assert_task_completed
69
+
70
+ visit '/foreman_openbolt/page_task_history'
71
+ assert_no_selector '[aria-label="Loading task history"]', wait: 15
72
+ assert_selector 'table[aria-label="Task history table"] tbody tr', minimum: 1, wait: 15
73
+
74
+ # The task name is rendered as a popover trigger whose aria-label is
75
+ # "View details for task <name>"
76
+ first('button[aria-label="View details for task acceptance::echo"]').click
77
+
78
+ assert_selector 'table[aria-label="Task parameters"]', wait: 10
79
+ within 'table[aria-label="Task parameters"]' do
80
+ assert_selector 'td', text: 'message'
81
+ assert_selector 'td', text: 'popover history param'
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../acceptance_helper'
4
+
5
+ # Tests that the proxy correctly loads and exposes tasks, and that
6
+ # task metadata (parameters) is displayed in the UI.
7
+ class TransportOptionsTest < AcceptanceTestCase
8
+ def setup
9
+ super
10
+ foreman_login
11
+ visit '/foreman_openbolt/page_launch_task'
12
+ assert_selector '#smart-proxy-input', wait: 15
13
+ select_first_proxy
14
+ end
15
+
16
+ def test_proxy_selection_populates_task_list
17
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
18
+ end
19
+
20
+ def test_all_fixture_tasks_discoverable
21
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
22
+ options = all('#task-name-input option').map(&:text)
23
+
24
+ %w[echo complex_params failing_task noop_task slow_task target_conditional].each do |task|
25
+ assert options.any?("acceptance::#{task}"),
26
+ "Expected acceptance::#{task} in task list, got: #{options}"
27
+ end
28
+ end
29
+
30
+ def test_echo_task_shows_message_param_as_empty_text_input
31
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
32
+ select 'acceptance::echo', from: 'task-name-input'
33
+ message = find_by_id('param_message', wait: 10)
34
+ assert_equal 'text', message['type']
35
+ assert_equal '', message.value
36
+ end
37
+
38
+ def test_complex_params_task_populates_fields_with_correct_defaults
39
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
40
+ select 'acceptance::complex_params', from: 'task-name-input'
41
+
42
+ # No-default fields render empty.
43
+ assert_equal '', find_by_id('param_required_string', wait: 10).value
44
+ assert_equal '', find_by_id('param_array_param').value
45
+ # with_default has a 'default_value' default in its task metadata;
46
+ # the launch page must populate it when the task is selected
47
+ # (handleTaskChange in LaunchTask/index.js).
48
+ assert_equal 'default_value', find_by_id('param_with_default').value
49
+ end
50
+
51
+ def test_slow_task_shows_seconds_param_with_default_value
52
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
53
+ select 'acceptance::slow_task', from: 'task-name-input'
54
+ # slow_task.json declares "default": 5
55
+ assert_equal '5', find_by_id('param_seconds', wait: 10).value
56
+ end
57
+
58
+ def test_target_conditional_shows_succeed_on_param_as_empty_text_input
59
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
60
+ select 'acceptance::target_conditional', from: 'task-name-input'
61
+ succeed_on = find_by_id('param_succeed_on', wait: 10)
62
+ assert_equal 'text', succeed_on['type']
63
+ assert_equal '', succeed_on.value
64
+ end
65
+
66
+ def test_switching_tasks_swaps_parameter_fields_and_defaults
67
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
68
+
69
+ select 'acceptance::echo', from: 'task-name-input'
70
+ assert_equal '', find_by_id('param_message', wait: 10).value
71
+
72
+ select 'acceptance::slow_task', from: 'task-name-input'
73
+ assert_no_selector '#param_message', wait: 5
74
+ # Asserting the seconds default of 5 (not just field presence)
75
+ # proves the new task's metadata loaded rather than stale state.
76
+ assert_equal '5', find_by_id('param_seconds', wait: 10).value
77
+ end
78
+
79
+ def test_deselecting_proxy_clears_task_selection_and_parameters
80
+ assert_selector '#task-name-input option', minimum: 2, wait: 15
81
+ select 'acceptance::echo', from: 'task-name-input'
82
+ assert_selector '#param_message', wait: 10
83
+
84
+ # Deselect by choosing the placeholder option (value="")
85
+ select 'Select Smart Proxy', from: 'smart-proxy-input'
86
+
87
+ # Task selection and its parameters should be cleared, showing the
88
+ # "Select a task to see parameters" empty state.
89
+ assert_no_selector '#param_message', wait: 10
90
+ assert_selector '.pf-v5-c-empty-state',
91
+ text: 'Select a task to see parameters', wait: 10
92
+ end
93
+
94
+ def test_transport_option_renders_as_select_with_ssh_and_winrm
95
+ # The transport OpenBolt option has type ["ssh", "winrm"] and
96
+ # ParameterField renders array-typed values as a <select>.
97
+ field = find_by_id('param_transport', wait: 10)
98
+ assert_equal 'select', field.tag_name
99
+ option_values = all('#param_transport option').map(&:value)
100
+ assert_equal %w[ssh winrm], option_values
101
+ assert_equal 'ssh', field.value
102
+ end
103
+
104
+ def test_switching_transport_to_winrm_hides_ssh_only_options
105
+ # SSH-only options (private-key, host-key-check) are present on load
106
+ # because the default transport is ssh.
107
+ assert_selector '#param_host-key-check', wait: 10
108
+ assert_selector '#param_private-key', wait: 10
109
+
110
+ # Switching transport to winrm should hide options whose metadata
111
+ # tags them as ssh-only (OpenBoltOptionsSection filters by transport).
112
+ select 'winrm', from: 'param_transport'
113
+ assert_no_selector '#param_host-key-check', wait: 10
114
+ assert_no_selector '#param_private-key', wait: 10
115
+
116
+ # Switching back restores them
117
+ select 'ssh', from: 'param_transport'
118
+ assert_selector '#param_host-key-check', wait: 10
119
+ assert_selector '#param_private-key', wait: 10
120
+ end
121
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'dynflow/testing'
5
+
6
+ # Load plugin factories and foreman-tasks factories
7
+ FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'unit', 'factories')
8
+ FactoryBot.definition_file_paths << File.join(ForemanTasks::Engine.root, 'test', 'factories')
9
+ FactoryBot.reload
10
+
11
+ module ForemanOpenbolt
12
+ class PluginTestCase < ActiveSupport::TestCase
13
+ teardown do
14
+ WebMock.reset!
15
+ end
16
+ end
17
+ end