class AddPlanView(BaseLoggedInView): title = Text(".//h2[normalize-space(.)='Plan / Playbook Builder']") name = TextInput(name="name") actions = SatTable(".//div[contains(@class, 'maintenance-plan')]//table", column_widgets={0: Checkbox(locator=".//input")}) rules_filter = TextInput( locator=".//input[@placeholder='Filter by rule name']") cancel = Button("Cancel") save = Button("Save") @property def is_displayed(self): return self.title.is_displayed
class ArchitecturesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Architectures']") new = Text("//a[contains(@href, '/architectures/new')]") table = SatTable('.//table', column_widgets={ 'Name': Text('./a'), 'Actions': Text('.//a[@data-method="delete"]'), }) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class BookmarksView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Bookmarks']") table = SatTable( ".//table", column_widgets={ 'Name': Text('./a'), 'Actions': Text("./span/a"), }, ) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class LDAPAuthenticationsView(BaseLoggedInView): title = Text("//h1[text()='LDAP authentication sources' or " "text()='LDAP Authentication']") new = Text("//a[contains(@href, '/auth_source_ldaps/new')]") table = SatTable('.//table', column_widgets={ 'Name': Text('./a'), 'Actions': Text('.//a[@data-method="delete"]'), }) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class SCAPContentsView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='SCAP Contents']") new = Text("//a[contains(@href, 'scap_contents/new')]") table = SatTable( './/table', column_widgets={ 'Title': 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 DiscoveryRulesView(BaseLoggedInView): title = Text("//h1[text()='Discovery Rules']") new = Text("//a[contains(@href, '/discovery_rules/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 SCAPPoliciesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[normalize-space(.)='Compliance Policies']") new = Text("//a[contains(@href, '/compliance/policies/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 ProductsTableView(BaseLoggedInView, SearchableViewMixin): title = Text("//h2[contains(., 'Products')]") new = Text("//button[contains(@href, '/products/new')]") edit = Text("//td/a[contains(@ui-sref, 'product.repositories') and " "contains(@href, 'products')]") repo_discovery = Text("//button[contains(.,'Repo Discovery')]") actions = ActionsDropdown("//div[contains(@class, 'btn-group')]") table = SatTable('.//table', column_widgets={'Name': Text('./a')}) dialog = ConfirmationDialog() @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class HostCollectionManageModuleStreamsView(BaseLoggedInView, SearchableViewMixin): title = Text("//h4[contains(., 'Content Host Module Stream Management')]") table = SatTable( locator='//table', column_widgets={ 'Name': Text('.//a'), 'Actions': ActionDropdownWithCheckbox(".//div[contains(@class, 'dropdown')]"), }, ) @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 SmartClassParametersView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Smart Class Parameters']") table = SatTable( './/table', column_widgets={ 'Parameter': Text('./a'), 'Puppet Class': Text("./a[contains(@href, '/puppetclasses')]"), }, ) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class interfaces(SatTab): interface = HostInterface() interfaces_list = SatTable(".//table[@id='interfaceList']", column_widgets={'Actions': TableActions()}) add_new_interface = Text("//button[@id='addInterface']") def before_fill(self, values=None): """If we don't want to break view.fill() procedure flow, we need to push 'Edit' button to open necessary dialog to be able to fill values """ self.interfaces_list[0]['Actions'].widget.edit.click() wait_for(lambda: self.interface.is_displayed is True, timeout=30, delay=1, logger=self.logger)
class PuppetClassesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Puppet Classes']") import_environments = Text("//a[contains(@href, '/import_environments')]") table = SatTable( './/table', column_widgets={ 'Class name': Text('./a'), 'Actions': Text('.//a[@data-method="delete"]'), } ) @property def is_displayed(self): return self.browser.wait_for_element( self.title, exception=False) is not None
class AddSubscriptionView(BaseLoggedInView): breadcrumb = BreadCrumb() table = SatTable( locator='.//table', column_widgets={ 'Subscription Name': Text('.//a'), 'Quantity to Allocate': TextInput(locator='.//input'), }, ) submit_button = Button('Submit') cancel_button = Button('Cancel') @property def is_displayed(self): return self.browser.wait_for_element(self.table, visible=True, exception=False) is not None
class HardwareModelsView(BaseLoggedInView, SearchableViewMixin): delete_dialog = DeleteHardwareModelDialog() title = Text("//h1[text()='Hardware Models']") new = Text("//a[contains(@href, '/models/new')]") table = SatTable( './/table', column_widgets={ 'Name': Text('.//a'), 'Actions': Button('Delete'), }, ) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class PartitionTablesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Partition Tables']") new = Button("Create Partition Table") 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 ErratumView(BaseLoggedInView): title = Text("//h1[contains(., 'Errata')]") table = SatTable( locator='.//table', column_widgets={ 0: Checkbox(locator=".//input[@type='checkbox']"), 'Errata ID': Text("./a"), } ) repo_filter = SatSelect(".//select[@ng-model='repository']") applicable_filter = Checkbox( locator=".//input[@ng-model='showApplicable']") installable_filter = Checkbox( locator=".//input[@ng-model='showInstallable']") apply_errata = Text( ".//button[contains(@class, 'btn-primary')]" "[@ng-click='goToNextStep()']" ) searchbox = Search() def search(self, query, applicable=True, installable=False, repo=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 bool applicable: filter by only applicable errata :param bool installable: filter by only installable errata :param str optional repo: filter by repository name :return: list of dicts representing table rows :rtype: list """ self.installable_filter.fill(installable) self.applicable_filter.fill(applicable) if repo is not None: self.repo_filter.fill(repo) if re.search(r'\w{4}-\d{4}:\d{4}', query): query = 'id = {}'.format(query) self.searchbox.search(query) return self.table.read() @property def is_displayed(self): return self.browser.wait_for_element( self.title, exception=False) is not None
class SmartVariablesTableView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Smart Variables']") new = Text("//a[contains(@href, '/variable_lookup_keys/new')]") table = SatTable( './/table', column_widgets={ 'Variable': Text('./a'), 'Puppet Class': Text("./a[contains(@href, '/puppetclasses')]"), 'Actions': Text('.//a[@data-method="delete"]'), }, ) @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class ComputeResourcesView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Compute Resources']") new = Text("//a[contains(@href, '/compute_resources/new')]") table = SatTable( './/table', column_widgets={ 'Name': Text('./a'), 'Actions': ActionsDropdown("./div[contains(@class, 'btn-group')]"), }) @property def is_displayed(self): """Check if the right page is displayed""" return self.browser.wait_for_element(self.title, exception=False) is not None
class HostGroupsView(BaseLoggedInView, SearchableViewMixin): title = Text( "//h1[contains(., 'Host Group Configuration') or text()='Host Groups']" ) new = Text("//a[contains(@href, '/hostgroups/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 and self.browser.url.endswith('hostgroups'))
class HostsView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Hosts']") new = Text("//a[contains(@href, '/hosts/new')]") table = SatTable( './/table', column_widgets={ 0: Checkbox(locator=".//input[@class='host_select_boxes']"), 'Name': Text("./a"), 'Actions': ActionsDropdown("./div[contains(@class, 'btn-group')]"), }) actions = ActionsDropdown("//div[@id='submit_multiple']") @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None
class ComputeResourcesView(BaseLoggedInView, SearchableViewMixin): title = Text('//*[(self::h1 or self::h5) and text()="Compute Resources"]') new = Text('//a[text()="Create Compute Resource"]') table = SatTable( './/table', column_widgets={ 'Name': Text('./a'), 'Actions': ActionsDropdown("./div[contains(@class, 'btn-group')]"), }, ) @property def is_displayed(self): """Check if the right page is displayed""" return self.browser.wait_for_element(self.title, exception=False) is not None
class DomainListView(BaseLoggedInView, SearchableViewMixin): """List of all domains.""" # Delete button isn't the typical "btn" # It sits within a span, we'll access the href via a Text widget table = SatTable( locator='table#domains_list', column_widgets={ 'Description': Text("./a"), 'Hosts': Text("./a"), 'Actions': Text(".//a[@data-method='delete']") # delete button }) create_button = Text(".//a[@href='/domains/new']") @property def is_displayed(self): return self.browser.wait_for_element(self.table, exception=False) is not None
class TasksView(BaseLoggedInView, SearchableViewMixin): title = Text("//h1[text()='Tasks']") focus = ActionsDropdown( "//div[./button[@id='tasks-dashboard-time-period-dropdown']]") table = SatTable( ".//div[@class='tasks-table']//table", column_widgets={ 'Action': Text('./a'), }, ) pagination = TaskPagination() @property def is_displayed(self): return self.browser.wait_for_element(self.title, exception=False) is not None @View.nested class RunningChart(View): ROOT = ".//div[@id='running-tasks-card']" name = Text("./h2") total = PieChart("./div[@class='card-pf-body']") @View.nested class PausedChart(View): ROOT = ".//div[@id='paused-tasks-card']" name = Text("./h2") total = PieChart("./div[@class='card-pf-body']") @View.nested class StoppedChart(View): ROOT = ".//div[@id='stopped-tasks-card']" name = Text("./h2") table = Table( locator='.//table', column_widgets={ 'Total': Text('./button'), }, ) @View.nested class ScheduledChart(View): ROOT = ".//div[@id='scheduled-tasks-card']" name = Text("./h2") total = Text(".//div[@class='scheduled-data']")
class matchers(View): table = SatTable( ".//table[contains(@class, 'white-header')]", column_widgets={ 'Attribute type': MatcherAttribute(), 'Value': TextInputHidden(locator=".//textarea[contains(@id, 'value')]"), 'Omit': Checkbox( locator= ".//input[contains(@name, '[omit]') and @type!='hidden']"), }, ) add_new_matcher = Text( ".//a[contains(@data-original-title, 'add a new matcher')]") def fill(self, values): """Add and fill all matchers provided Example:: [ { 'Attribute type': { 'matcher_attribute_type': 'os', 'matcher_attribute_value': 'x86' }, 'Value': 'newvalue' }, { 'Attribute type': { 'matcher_attribute_type': 'fqdn', 'matcher_attribute_value': 'myhost.com' }, 'Value': 'newvalue2' } ] """ for matcher_value in values: self.add_new_matcher.click() self.table[-1].fill(matcher_value)
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']"), 'RPM Name': TextInput(locator='.//input'), 'Architecture': TextInput(locator='.//input'), 'Version': CVFRuleVersion(), ACTIONS_COLUMN: CVFRuleActions(), }, )
class ComputeProfileDetailView(BaseLoggedInView): breadcrumb = BreadCrumb() table = SatTable( './/table', column_widgets={ 'Compute Resource': 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] == 'Compute Profiles' and self.breadcrumb.read() != 'Create Compute Profile' and self.breadcrumb.read() != 'Edit Compute Profile' )
class external_groups(SatTab): TAB_NAME = 'External Groups' table = SatTable('.//table', column_widgets={ 'Actions': Text('.//a[contains(@href, "refresh")]'), }) add_external_user_group = Text( './/a[@data-association="external_usergroups"]') name = TextInput(locator=( "(//input[starts-with(@name, 'usergroup[external_usergroups_attributes]')]" "[contains(@name, '[name]')])[last()]")) auth_source = FilteredDropdown(locator=( "//div[starts-with(@id, 's2id_usergroup_external_usergroups_attributes')]" "[contains(@id, 'auth_source_id')]")) def before_fill(self, values): self.add_external_user_group.click()
class puppet_modules(SatTab): TAB_NAME = 'Puppet Modules' cv_filter = SatSelect(".//select[@ng-model='contentView']") searchbox = Search() table = SatTable(locator=".//table") def search(self, query, cv=None): """Apply available filters before proceeding with searching. :param str query: search query to type into search field. :param str optional cv: filter by content view name :return: list of dicts representing table rows :rtype: list """ if cv: self.cv_filter.fill(cv) self.searchbox.search(query) return self.table.read()
class lce(ParametrizedView): ROOT = ParametrizedLocator( ".//div[@ng-repeat='path in paths']" "[table//th/a[normalize-space(.)='{lce_name}']]" ) PARAMETERS = ('lce_name',) LAST_ENV = "//div[@ng-repeat='path in paths']//table//th[last()]" current_env = Text(ParametrizedLocator( ".//a[normalize-space(.)='{lce_name}']")) envs_table = SatTable(locator=".//table") new_child = Text(".//a[contains(@href, '/lifecycle_environments/')]") @classmethod def all(cls, browser): """Helper method which returns list of tuples with all available LCE names (last available environment is used as a name). It's required for :meth:`read` to work properly. """ return [ (element.text,) for element in browser.elements(cls.LAST_ENV)] def read(self): """Returns content views and count hosts count per each available lifecycle environment We get dictionary in next format: { 'LCE_1': {'Content Views': 0, 'Content Hosts': 1}, 'LCE_2': {'Content Views': 1, 'Content Hosts': 2}, } """ result = {} available_envs = self.envs_table.headers[1:] lce_metric_names = [row[0].text for row in self.envs_table] for column_name in available_envs: metric_values = ( int(row[column_name].text) for row in self.envs_table) result[column_name] = {} for row_name in lce_metric_names: result[column_name][row_name] = next(metric_values) return result
class ContentViewFiltersView(BaseLoggedInView, SearchableViewMixin): breadcrumb = BreadCrumb() 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'), }, ) @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() == 'Yum Filters')