class DiscoveredHostDetailsView(BaseLoggedInView): """Discovered Host details view""" breadcrumb = BreadCrumb() back = Text( ".//a[contains(@class, 'btn') and @data-id='aid_discovered_hosts']") actions = ActionsDropdown( "//div[contains(@class, 'btn-group')][a[@data-toggle='dropdown']]") delete = Text(".//a[contains(@data-confirm, 'Delete')]") expand_all = Text(".//a[@id='expand_all']") interfaces = SatTable("//div[@id='interfaces-panel']/table") highlights = DiscoveredHostDetailsTable( "//div[@id='category-highlights']//table") storage = DiscoveredHostDetailsTable( "//div[@id='category-storage']//table") hardware = DiscoveredHostDetailsTable( "//div[@id='category-hardware']//table") network = DiscoveredHostDetailsTable( "//div[@id='category-network']//table") software = DiscoveredHostDetailsTable( "//div[@id='category-software']//table") miscellaneous = DiscoveredHostDetailsTable( "//div[@id='category-miscellaneous']//table") @property def is_displayed(self): breadcrumb_loaded = self.browser.wait_for_element(self.breadcrumb, exception=False) return (breadcrumb_loaded and self.breadcrumb.locations[0] == 'Discovered hosts' and self.breadcrumb.locations[1].startswith('Discovered host:'))
class errata(SatTab): lce_filter = Select( locator='.//select[@ng-model="selectedErrataOption"]') searchbox = Search() apply_selected = ActionsDropdown( ".//span[contains(@class, 'btn-group')]") recalculate = Button('Recalculate') table = SatTable('.//table', column_widgets={ 0: Checkbox(locator="./input[@type='checkbox']"), 'Id': Text('./a'), }) def search(self, query, lce=None): """Apply available filters before proceeding with searching and automatically set proper search mask if errata id instead of errata title was passed. :param str query: search query to type into search field. Both errata id (RHEA-2012:0055) and errata title (Sea_Erratum) are supported. :param str optional lce: filter by lifecycle environment :return: list of dicts representing table rows :rtype: list """ if lce is not None: self.lce_filter.fill(lce) if re.search(r'\w{4}-\d{4}:\d{4}', query): query = 'id = {}'.format(query) self.searchbox.search(query) return self.table.read()
class PuppetEnvironmentTableView(BaseLoggedInView, SearchableViewMixin): """ Basic view after clicking Configure -> Environments. In basic view, there can be seen title Puppet Environments, button Create Puppet Environment (new), button import environments and table with existing Puppet Environments """ title = Text(".//h1[contains(., 'Puppet Environments')]") new = Text(".//a[contains(@href, '/environments/new')]") import_environments = Text( ".//span[contains(@class, 'btn')]" "/a[contains(@href, 'import_environments')]" ) table = SatTable( locator='.//table', column_widgets={ 'Name': Text(".//a[starts-with(@href, '/environments/') and \ contains(@href,'/edit')]"), 'Actions': ActionsDropdown( './div[contains(@class, "btn-group")]') } ) @property def is_displayed(self): return self.browser.wait_for_element( self.title, exception=False) is not None
class versions(SatTab): searchbox = Search() table = SatTable( locator='//table', column_widgets={ 'Version': Text('.//a'), 'Status': PublishPromoteProgressBar(), 'Actions': ActionsDropdown('./div[contains(@class, "btn-group")]') }, ) def search(self, version_name): """Searches for content view version. Searchbox can't search by version name, only by id, that's why in case version name was passed, it's transformed into recognizable value before filling, for example:: 'Version 1.0' -> 'version = 1' """ search_phrase = version_name if version_name.startswith('V') and '.' in version_name: search_phrase = 'version = {}'.format( version_name.split()[1].split('.')[0]) self.searchbox.search(search_phrase) return self.table.read()
class repositories(SatTab): table = SatTable( locator=".//table", column_widgets={ 'Name': Text("./a"), } )
class repository_sets(SatTab, SearchableViewMixin): TAB_NAME = 'Repository Sets' show_all = Checkbox( locator=".//input[contains(@ng-model, 'contentAccessModeAll')]") limit_to_lce = Checkbox( locator=".//input[contains(@ng-model, 'contentAccessModeEnv')]") actions = ActionsDropdown("//div[contains(@class, 'btn-group')]") table = SatTable( './/table', column_widgets={ 0: Checkbox(locator="./input[@type='checkbox']"), 'Product Name': Text('./a'), }, ) def read(self): """Sometimes no checkboxes are checked off by default, selecting "Show All" in such case. """ if self.show_all.read() is False and self.limit_to_lce.read( ) is False: self.show_all.fill(True) return super().read()
class ImportPuppetEnvironmentView(BaseLoggedInView, SearchableViewMixin): """ View after clicking Configure -> Environments -> import environments with toggles New, Updated, Obsolete. Button update and cancel """ breadcrumb = BreadCrumb() new = Text(".//a[contains(@data-original-title,'new')]") updated = Text(".//a[contains(@data-original-title,'updated')]") obsolete = Text(".//a[contains(@data-original-title,'obsolete')]") update = Text(".//input[@name='commit']") cancel = Text(".//a[contains(@class, 'btn') and @href='/environments']") table = SatTable( locator='.//table', column_widgets={ 'Environment': Text('./a'), } ) @property def is_displayed(self): breadcrumb_loaded = self.browser.wait_for_element( self.breadcrumb, exception=False) return ( breadcrumb_loaded and self.breadcrumb.locations[0] == 'Environments' and self.breadcrumb.read() == 'Changed Environments' )
class ContentViewVersionRemoveView(BaseLoggedInView): breadcrumb = BreadCrumb() table = SatTable( locator='.//table', column_widgets={ 0: Checkbox(locator="./input[@type='checkbox']"), } ) completely = Checkbox( locator=".//input[@ng-model='deleteOptions.deleteArchive']") next = Text(".//button[@ng-click='processSelection()']") cancel = Text(".//button[normalize-space(.)='Cancel']") @property def is_displayed(self): breadcrumb_loaded = self.browser.wait_for_element( self.breadcrumb, exception=False) return ( breadcrumb_loaded and self.breadcrumb.locations[0] == 'Content Views' and len(self.breadcrumb.locations) == 3 and self.breadcrumb.read() == 'Deletion' and self.next.is_displayed )
class module_streams(SatTab, SearchableViewMixin): TAB_NAME = 'Module Streams' status_filter = Select( locator='.//select[@ng-model="nutupaneParams.status"]') table = SatTable( locator='//table', column_widgets={ 'Name': Text('.//a'), 'Actions': ActionDropdownWithCheckbox( ".//div[contains(@class, 'dropdown')]") }, ) def search(self, query, status='All'): """Searches for Module Streams. Apply available filters before proceeding with searching. By default 'All' is passed :param str query: search query to type into search field. :param str optional status: filter by status of module stream on host :return: list of dicts representing table rows :rtype: list """ if status is not None: self.status_filter.fill(status) self.searchbox.search(query) return self.table.read()
class ModuleStreamView(BaseLoggedInView): """Main Module_Streams view""" title = Text("//h2[contains(., 'Module Streams')]") table = SatTable('.//table', column_widgets={'Name': Text("./a")}) search_box = CustomSearch() def search(self, query): """Perform search using search box on the page and return table contents. :param str query: search query to type into search field. E.g. ``name = "bar"``. :return: list of dicts representing table rows :rtype: list """ self.search_box.search(query) return self.table.read() @property def is_displayed(self): """The view is displayed when it's title exists""" return self.browser.wait_for_element(self.title, exception=False) is not None
class packages_installed(SatTabWithDropdown, SearchableViewMixin): TAB_NAME = 'Packages' SUB_ITEM = 'Installed' remove_selected = Button('Remove Selected') table = SatTable( './/table', column_widgets={0: Checkbox(locator="./input[@type='checkbox']")})
class HostSubscription(View): ROOT = ".//li[@data-name='Host Subscription Status']" subscriptions = SatTable('.//table', column_widgets={0: Text('./a')}) def fill(self, values): if 'type' not in values: raise ValueError('You need provide subscription task type') self.subscriptions.row((0, 'contains', str(values['type'])))[0].widget.click()
class LatestFailedTasks(View): ROOT = ".//li[@data-name='Latest Warning/Error Tasks']" tasks = SatTable('.//table', column_widgets={'Name': Text('./a')}) def fill(self, values): if 'name' not in values: raise ValueError('You need provide name of the task') self.tasks.row(name=values['name'])['Name'].widget.click()
class JobInvocationsView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[contains(., 'Job') and contains(., 'nvocations')]") new = Text("//a[contains(@href, '/job_invocations/new')]") table = SatTable('.//table', column_widgets={'Description': Text('./a')}) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class ContentViewTableView(BaseLoggedInView, SearchableViewMixin): title = Text("//h2[contains(., 'Content Views')]") new = Text("//a[contains(@href, '/content_views/new')]") table = SatTable('.//table', column_widgets={'Name': Text('./a')}) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class packages_applicable(SatTabWithDropdown, SearchableViewMixin): TAB_NAME = 'Packages' SUB_ITEM = 'Applicable' upgrade_selected = ActionsDropdown(".//span[contains(@class, 'btn-group')]") update_all_packages = Button('Update All Packages') table = SatTable( './/table', column_widgets={0: Checkbox(locator="./input[@type='checkbox']")} )
class affected_repositories(SatSecondaryTab): TAB_NAME = 'Affected Repositories' filter_toggle = RadioGroup(".//div[@class='col-sm-8']") product_filter = Select(locator=".//select[@ng-model='product']") searchbox = Search() update_repositories = Button('Update Repositories') table = SatTable( locator='//table', column_widgets={0: Checkbox(locator=".//input[@type='checkbox']")}, )
class DiscoveredHostsActionDialog(BaseLoggedInView): """Common dialog view for all discovered hosts actions""" title = None table = SatTable("//div[@class='modal-body']//table") submit = Text("//button[@onclick='tfm.hosts.table.submitModalForm()']") @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class SelectPuppetModuleVersionView(BaseLoggedInView, SearchableViewMixin): title = Text('//h3/span[contains(., "Select an Available Version of")]') table = SatTable(locator='.//table', column_widgets={ 'Actions': Text('./button[@ng-click="selectVersion(item)"]') }) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class AddNewPuppetModuleView(BaseLoggedInView, SearchableViewMixin): title = Text('//h3/span[text()="Select A New Puppet Module To Add"]') table = SatTable(locator='.//table', column_widgets={ 'Actions': Text('./button[@ng-click="selectVersion(item.name)"]') }) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class TaskStatus(View): ROOT = ".//li[@data-name='Task Status']" states = SatTable( './/table', column_widgets={'No. of Tasks': Text('./a')}, ) def fill(self, values): if 'state' not in values or 'result' not in values: raise ValueError('both state and result values have to be provided') self.states.row(state=values['state'], result=values['result'])[ 'No. of Tasks' ].widget.click()
class rpms(SatSecondaryTab, SearchableViewMixin): TAB_NAME = 'RPMs' exclude_no_errata = Checkbox(locator=".//input[@type='checkbox']" "[@ng-model='filter.original_packages']") add_rule = Text(".//button[@ng-click='addRule()']") remove_rule = Text(".//button[@ng-click='removeRules(filter)']") table = SatTable( locator='//table', column_widgets={ 0: Checkbox(locator=".//input[@type='checkbox']"), 4: Text("./button[contains(@ng-click, 'rule.editMode')]"), }, )
class filters(SatTabWithDropdown, SearchableViewMixin): TAB_NAME = 'Yum Content' SUB_ITEM = 'Filters' new_filter = Text(".//button[@ui-sref='content-view.yum.filters.new']") remove_selected = Text(".//button[@ng-click='removeFilters()']") table = SatTable( locator='//table', column_widgets={ 0: Checkbox(locator=".//input[@type='checkbox']"), 'Name': Text('./a'), }, )
class ProvisioningTemplatesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[contains(., 'Provisioning Templates')]") new = Text("//a[contains(@href, '/templates/provisioning_templates/new')]") table = SatTable( './/table', column_widgets={ 'Name': Text('./a'), 'Actions': ActionsDropdown("./div[contains(@class, 'btn-group')]"), } ) @property def is_displayed(self): return self.browser.wait_for_element( self.title, exception=False) is not None
class SelectPuppetModuleVersionView(BaseLoggedInView, SearchableViewMixin): breadcrumb = BreadCrumb() table = SatTable(locator='.//table', column_widgets={ 'Actions': Text('./button[@ng-click="selectVersion(item)"]') }) @property def is_displayed(self): breadcrumb_loaded = self.browser.wait_for_element(self.breadcrumb, exception=False) return (breadcrumb_loaded and self.breadcrumb.locations[0] == 'Content Views' and self.breadcrumb.read() == 'Version for Module:')
class overview(SatTab): job_status = Text( "//div[@id='job_invocations_chart_container']" "//*[name()='tspan'][contains(@class,'donut-title-small-pf')]") job_status_progress = Text( "//div[@id='job_invocations_chart_container']" "//*[name()='tspan'][contains(@class,'donut-title-big-pf')]") hosts_table = SatTable( './/table', column_widgets={ 'Host': Text('./a'), 'Actions': ActionsDropdown("./div[contains(@class, 'btn-group')]"), }) total_hosts = Text("//h2[contains(., 'Total hosts')]" "/span[@class='card-pf-aggregate-status-count']")
class EntitySearchView(SatSecondaryTab): repo_filter = SatSelect(".//select[@ng-model='repository']") searchbox = Search() table = SatTable(".//table") def search(self, query, repo=None): """Apply available filters before proceeding with searching. :param str query: search query to type into search field. :param str optional repo: filter by repository name :return: list of dicts representing table rows :rtype: list """ if repo: self.repo_filter.fill(repo) self.searchbox.search(query) return self.table.read()
class AnsibleVariablesView(BaseLoggedInView, SearchableViewMixin): """Main Ansible Variables view""" title = Text("//h1[contains(., text()='Ansible Variables')") new_variable = Text("//a[contains(@href, '/ansible/ansible_variables/new')]") total_variables = Text(".//span[@class='pagination-pf-items-total']") table = SatTable( './/table', column_widgets={ 'Actions': Text(".//a[@data-method='delete']"), }, ) pagination = Pagination() @property def is_displayed(self): return self.title.is_displayed and self.new_variable.is_displayed
class ContentHostsView(BaseLoggedInView, SearchableViewMixin): title = Text("//h2[contains(., 'Content Hosts')]") export = Text( ".//a[contains(@class, 'btn')][contains(@href, 'content_hosts.csv')]") register = Text(".//button[@ui-sref='content-hosts.register']") actions = ActionsDropdown(".//div[contains(@class, 'btn-group')]") dialog = ConfirmationDialog() table = SatTable('.//table', column_widgets={ 0: Checkbox(locator="./input[@type='checkbox']"), 'Name': Text('./a'), 'Subscription Status': StatusIcon(), 'Installable Updates': InstallableUpdatesCellView(), }) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class PackagesView(BaseLoggedInView): """Main Packages view""" title = Text("//h2[contains(., 'Packages')]") table = SatTable('.//table', column_widgets={'RPM': Text("./a")}) repository = Select(locator=".//select[@ng-model='repository']") applicable = Checkbox(locator=".//input[@ng-model='showApplicable']") upgradable = Checkbox(locator=".//input[@ng-model='showUpgradable']") search_box = Search() def search(self, query, repository='All Repositories', applicable=False, upgradable=False): """Perform search using search box on the page and return table contents. :param str query: search query to type into search field. E.g. ``name = "bar"``. :param str repository: repository name to select when searching for the package. :param bool applicable: To show only applicable packages :param bool upgradable: To show only upgradable packages :return: list of dicts representing table rows :rtype: list """ self.repository.fill(repository) # set the upgradable first as if enabled, applicable element will be # disabled self.upgradable.fill(upgradable) if not upgradable: self.applicable.fill(applicable) self.search_box.search(query) return self.table.read() @property def is_displayed(self): """The view is displayed when it's title exists""" return self.browser.wait_for_element(self.title, exception=False) is not None