From 65d801f2bf4242340af1a341d6732352b79189c5 Mon Sep 17 00:00:00 2001 From: Ladislav Vasina Date: Thu, 15 Feb 2024 13:56:46 +0100 Subject: [PATCH] [6.15.z] Change content source cherry-pick --- airgun/entities/host.py | 66 +++++++++++++++++++++++++++++++ airgun/entities/job_invocation.py | 27 +++++++++++++ airgun/views/host.py | 28 +++++++++++++ airgun/views/job_invocation.py | 22 ++++++----- 4 files changed, 134 insertions(+), 9 deletions(-) diff --git a/airgun/entities/host.py b/airgun/entities/host.py index 884dbf213..126d97441 100644 --- a/airgun/entities/host.py +++ b/airgun/entities/host.py @@ -1,6 +1,7 @@ from time import sleep from navmazing import NavigateToSibling +from wait_for import wait_for from airgun.entities.base import BaseEntity from airgun.exceptions import DisabledWidgetError @@ -16,6 +17,7 @@ HostsAssignCompliancePolicy, HostsAssignLocation, HostsAssignOrganization, + HostsChangeContentSourceView, HostsChangeEnvironment, HostsChangeGroup, HostsChangeOpenscapCapsule, @@ -163,6 +165,69 @@ def apply_action(self, action_name, entities_list, values=None): view.flash.assert_no_error() view.flash.dismiss() + def change_content_source( + self, + entities_list, + content_source, + lce, + content_view, + run_job_invocation=False, + update_hosts_manually=False, + ): + """ + Apply Change Content Source action to one or more hosts + + Args: + entities_list (list): names of the hosts for which we would like to change the content source + content_source (str): name of the content source to be selected + lce (str): name of the LCE to be selected + content_view (str): name of the content view to be selected + run_job_invocation (bool): whether to run job invocation in order to change the host's content source + update_hosts_manually (bool): whether to update hosts manually in order to change the host's content source + """ + + view = self._select_action('Change Content Source', entities_list) + view.wait_displayed() + self.browser.plugin.ensure_page_safe() + wait_for(lambda: view.content_source_select.is_displayed, timeout=10, delay=1) + view.content_source_select.fill(content_source) + wait_for(lambda: view.lce_env_title.is_displayed, timeout=10, delay=1) + # click on the specific LCE radio button + self.browser.click( + f'//input[@type="radio" and following-sibling::label[normalize-space(.)="{lce}"]]' + ) + view = HostsChangeContentSourceView(self.browser) + view.wait_displayed() + self.browser.plugin.ensure_page_safe() + view.content_view_select.click() + wait_for(lambda: view.content_view_select.is_displayed, timeout=10, delay=1) + view.wait_displayed() + self.browser.plugin.ensure_page_safe() + + self.browser.click(f'.//*[contains(text(), "{content_view}")][1]') + if run_job_invocation: + view.run_job_invocation.click() + view.wait_displayed() + self.browser.plugin.ensure_page_safe(timeout='5s') + elif update_hosts_manually: + view.update_hosts_manualy.click() + view.wait_displayed() + self.browser.plugin.ensure_page_safe(timeout='5s') + + def change_content_source_get_script(self, entities_list, content_source, lce, content_view): + """ + Function that reads generated script which is generated while choosing to update hosts manually. + """ + + self.change_content_source( + entities_list, content_source, lce, content_view, update_hosts_manually=True + ) + view = HostsChangeContentSourceView(self.browser) + wait_for(lambda: view.show_more_change_content_source.is_displayed, timeout=10, delay=1) + view.show_more_change_content_source.click() + script = view.generated_script.read() + return script + def export(self): """Export hosts list. @@ -397,6 +462,7 @@ class HostsSelectAction(NavigateStep): """ ACTIONS_VIEWS = { + 'Change Content Source': HostsChangeContentSourceView, 'Change Environment': HostsChangeEnvironment, 'Change Group': HostsChangeGroup, 'Assign Compliance Policy': HostsAssignCompliancePolicy, diff --git a/airgun/entities/job_invocation.py b/airgun/entities/job_invocation.py index 4655609a8..ecadb284f 100644 --- a/airgun/entities/job_invocation.py +++ b/airgun/entities/job_invocation.py @@ -1,4 +1,7 @@ +import time + from navmazing import NavigateToSibling +from selenium.webdriver.common.by import By from wait_for import wait_for from airgun.entities.base import BaseEntity @@ -42,6 +45,30 @@ def wait_job_invocation_state(self, entity_name, host_name, expected_state='succ logger=view.logger, ) + def submit_prefilled_view(self): + """This entity loads pre filled job invocation view and submits it.""" + time.sleep(3) + view = JobInvocationCreateView(self.browser) + time.sleep(3) + view.submit.click() + + def get_job_category_and_template(self): + """Reads selected job category and template for job invocation.""" + time.sleep(3) + view = JobInvocationCreateView(self.browser) + time.sleep(3) + element = self.browser.selenium.find_element(By.XPATH, '//div/input') + read_values = view.category_and_template.read() + read_values['job_template'] = element.get_attribute('value') + return read_values + + def get_targeted_hosts(self): + """Read targeted hosts for job invocation.""" + time.sleep(3) + view = JobInvocationCreateView(self.browser) + time.sleep(3) + return view.target_hosts_and_inputs.read() + @navigator.register(JobInvocationEntity, 'All') class ShowAllJobs(NavigateStep): diff --git a/airgun/views/host.py b/airgun/views/host.py index e559888fe..7ddb3cf77 100644 --- a/airgun/views/host.py +++ b/airgun/views/host.py @@ -19,6 +19,7 @@ BreadCrumb as PF4BreadCrumb, Button as PF4Button, FormSelect, + Select as PF4Select, ) from widgetastic_patternfly4.tabs import Tab @@ -709,6 +710,29 @@ class HostsChangeGroup(HostsActionCommonDialog): host_group = Select(id='hostgroup_id') +class HostsChangeContentSourceView(View): + title = Text('//h5') + + hosts_to_update = Text('//span[@class="pf-c-label pf-m-green"]//a') + ignored_hosts = Text('//span[@class="pf-c-label pf-m-orange"]//a') + + content_source_select = PF4Select('content-source-select') + disabled_environment_status = Text('//div[@aria-label="Info Alert"]') + + lce_env_title = Text('//div[normalize-space(.)="Lifecycle environment"]') + lce_env_path_list = Text('//div[@class="env-path"]/div/div') + + content_view_select = PF4Select('SelectContentView') + content_view_select_btn = Text(locator='//button[@aria-label="Options menu" and @tabindex]') + + run_job_invocation = Text(locator='//*[normalize-space(.)="Run job invocation"]') + update_hosts_manualy = Text(locator='//*[normalize-space(.)="Update hosts manually"]') + + show_more_change_content_source = Text('//button[normalize-space(.)="Show more"]') + show_less_change_content_source = Text('//button[normalize-space(.)="Show less"]') + generated_script = Text('//code') + + class HostsChangeEnvironment(HostsActionCommonDialog): title = Text( "//h4[normalize-space(.)='Change Environment - " @@ -815,6 +839,10 @@ class HostsDeleteActionDialog(HostsActionCommonDialog): ) +class ChangeContentSourceView(View): + """Hosts Change Content Source View""" + + class HostsDeleteTaskDetailsView(TaskDetailsView): """Hosts Delete Task Details View""" diff --git a/airgun/views/job_invocation.py b/airgun/views/job_invocation.py index 0ee540ff8..ef8b53660 100644 --- a/airgun/views/job_invocation.py +++ b/airgun/views/job_invocation.py @@ -1,8 +1,8 @@ from wait_for import wait_for from widgetastic.widget import Text, TextInput, View from widgetastic_patternfly import BreadCrumb -from widgetastic_patternfly4 import Button, Radio -from widgetastic_patternfly4.ouia import Select +from widgetastic_patternfly4 import Button, ChipGroup, Radio, Select +from widgetastic_patternfly4.ouia import Select as OUIASelect from airgun.views.common import ( BaseLoggedInView, @@ -30,25 +30,29 @@ class JobInvocationCreateView(BaseLoggedInView): @View.nested class category_and_template(WizardStepView): expander = Text(".//button[contains(.,'Category and template')]") - job_category = Select('OUIA-Generated-Select-single-1') - job_template = Select('OUIA-Generated-Select-typeahead-1') + job_category = Select(locator='//div[button[@aria-label="Job category toggle"]]') + job_template = Select(locator='//div[button[@aria-label="Job template toggle"]]') @View.nested class target_hosts_and_inputs(WizardStepView): expander = Text(".//button[contains(.,'Target hosts and inputs')]") command = TextInput(id='command') - package_action = Select('OUIA-Generated-Select-single-15') + selected_hosts = ChipGroup(locator='//div[@class="selected-chips"]/div') + + package_action = OUIASelect('OUIA-Generated-Select-single-15') package = TextInput(id='package') - service_action = Select('OUIA-Generated-Select-single-28') + action = Select(locator='//div[button[@aria-label="action toggle"]]') service = TextInput(id='service') - module_action = Select('OUIA-Generated-Select-single-31') + module_action = OUIASelect('OUIA-Generated-Select-single-31') module_spec = TextInput(id='module_spec') options = TextInput(id='options') - power_action = Select('OUIA-Generated-Select-single-34') + ansible_collections_list = TextInput(id='ansible_collections_list') + ansible_roles_list = TextInput(id='ansible_roles_list') + power_action = OUIASelect('OUIA-Generated-Select-single-34') @View.nested class advanced_fields(WizardStepView): @@ -97,7 +101,7 @@ class schedule_recurring_execution(WizardStepView): start_at_date = TextInput(locator='//input[contains(@aria-label, "starts at datepicker")]') start_at_time = TextInput(locator='//input[contains(@aria-label, "starts at timepicker")]') # Repeats - repeats = Select('OUIA-Generated-Select-single-3') + repeats = OUIASelect('OUIA-Generated-Select-single-3') repeats_at = TextInput(locator='//input[contains(@aria-label, "repeat-at")]') # Ends ends_never = Radio(id='never-ends')