def test_custom_button_order_ansible_playbook_service(setup_provider, vmware_vm, custom_vm_button, service_request, service, appliance): view = navigate_to(vmware_vm, "Details") view.toolbar.custom_button(custom_vm_button.group.text).item_select(custom_vm_button.text) submit_button = WButton(appliance.browser.widgetastic, "Submit") submit_button.click() wait_for(service_request.exists, num_sec=600) service_request.wait_for_request() view = navigate_to(service, "Details") assert view.provisioning.details.get_text_of("Hosts") == "localhost" assert view.provisioning.results.get_text_of("Status") == "successful"
def test_embedded_ansible_custom_button(full_template_vm, custom_vm_button, service_request, service, appliance): view = navigate_to(full_template_vm, "Details") view.toolbar.custom_button(custom_vm_button.group.text).item_select(custom_vm_button.text) submit_button = WButton(appliance.browser.widgetastic, "Submit") submit_button.click() wait_for(service_request.exists, num_sec=600) service_request.wait_for_request() view = navigate_to(service, "Details") if custom_vm_button.inventory == "Localhost": assert view.provisioning.details.get_text_of("Hosts") == "localhost" else: assert view.provisioning.details.get_text_of("Hosts") == full_template_vm.ip_address assert view.provisioning.results.get_text_of("Status") == "successful"
class ResourcePoolDetailsToolbar(View): """The toolbar on the details page""" configuration = Dropdown('Configuration') policy = Dropdown('Policy') download = Button(title='Download summary in PDF format')
class CustomSearch(Search): search_field = TextInput(locator=".//input[@role='combobox']") clear_button = Text(".//span[contains(@class,'fa-times')]") search_button = Button('Search')
class toolbar(View): # noqa configuration = Dropdown("Configuration") policy = Dropdown(text='Policy') download = Button(title="Print or export summary")
class SetOwnershipView(SetOwnershipForm): title = Text('#explorer_title_text') save_button = Button('Save') is_displayed = displayed_not_implemented
class CompanyTagsEditView(CompanyTagsAddView): """Edit Company Tags view""" save_button = Button('Save') reset_button = Button('Reset')
class TenantForm(ConfigurationView): """ Tenant Form """ name = Input(name='name') description = Input(name='description') cancel_button = Button('Cancel')
class NetworkRouterDetailsToolBar(View): """ Represents provider toolbar and its controls """ configuration = Dropdown(text='Configuration') policy = Dropdown(text='Policy') edit = Dropdown(text='Edit') download = Button(title='Print or export summary')
class NetworkProviderDetailsToolBar(NetworkProviderToolBar): """ Represents provider details toolbar """ monitoring = Dropdown(text='Monitoring') download = Button(title='Print or export summary')
class CloudNetworkDetailsToolBar(View): """ Represents provider details toolbar """ policy = Dropdown(text='Policy') download = Button(title='Print or export summary')
class BalancerDetailsToolBar(BalancerToolBar): """ Represents details toolbar of balancer summary """ download = Button(title='Print or export summary')
class ObjectStoreContainerDetailsToolbar(View): """The toolbar on the Object Store Containers detail page""" policy = Dropdown('Policy') download = Button(title='Download summary in PDF format')
class ClusterDetailsToolbar(View): """The toolbar on the detail page""" configuration = Dropdown('Configuration') policy = Dropdown('Policy') monitoring = Dropdown('Monitoring') download = Button('Download summary in PDF format')
class ZoneCollectLogToolbar(View): configuration = Dropdown('Configuration') edit = Button(title="Edit the Log Depot settings for the selected Zone") collect = Dropdown('Collect Logs')
class CollectLogsCredsEntities(View): username = Input(name='log_userid') password = Input(name='log_password') confirm_password = Input(name='log_verify') validate_button = Button('Validate')
class FlavorDetailsToolBar(View): policy = Dropdown('Policy') download = Button(title='Print or export summary') configuration = Dropdown('Configuration')
class NetworkRouterAddInterfaceView(BaseLoggedInPage): """ Represents Add Interface to Network Router page """ subnet_name = Select(id='cloud_subnet_id') add = Button('Add') is_displayed = displayed_not_implemented
class FlavorDetailsToolBar(View): policy = Dropdown('Policy') download = Button(title='Download summary in PDF format')
class SecurityGroupDetailsToolBar(View): """ Represents provider details toolbar """ policy = Dropdown(text='Policy') download = Button(title='Print or export summary') view_selector = View.nested(ItemsToolBarViewSelector)
class MapTagsEditView(MapTagsAddView): """Edit Map Tags view""" save_button = Button('Save') reset_button = Button('Reset')
class SubnetDetailsToolBar(View): """ Represents provider details toolbar """ configuration = Dropdown(text='Configuration') policy = Dropdown(text='Policy') download = Button(title='Print or export summary')
class toolbar(View): # noqa reload = Button(title='Refresh this page') policy = Dropdown(text='Policy') download = Dropdown(text='Download') view_selector = View.nested(ItemsToolBarViewSelector)
class OneProviderComponentsToolbar(View): policy = Dropdown(text='Policy') download = Dropdown(text='Download') back = Button(name='show_summary') view_selector = View.nested(ItemsToolBarViewSelector)
class schema(WaitTab): # noqa schema_title = Text('//div[@class="form_div"]/h3') @ParametrizedView.nested class fields(ParametrizedView): # noqa PARAMETERS = ('name', ) # Points to the <tr> ROOT = ParametrizedLocator( './/input[starts-with(@id, "fields_name_") and @value={name|quote}]/../..' ) ALL_FIELDS = './/input[starts-with(@name, "fields_name_")]' @cached_property def row_id(self): attr = self.browser.get_attribute( 'id', './td/input[starts-with(@id, "fields_name_")', parent=self) return int(attr.rsplit('_', 1)[-1]) name = Input(name=ParametrizedString('fields_name_{@row_id}')) type = BootstrapSelect( ParametrizedString('fields_aetype_{@row_id}')) data_type = BootstrapSelect( ParametrizedString('fields_datatype_{@row_id}')) default_value = Input( name=ParametrizedString('fields_default_value_{@row_id}')) display_name = Input( name=ParametrizedString('fields_display_name_{@row_id}')) description = Input( name=ParametrizedString('fields_description_{@row_id}')) substitute = Checkbox( name=ParametrizedString('fields_substitute_{@row_id}')) collect = Input( name=ParametrizedString('fields_collect_{@row_id}')) message = Input( name=ParametrizedString('fields_message_{@row_id}')) on_entry = Input( name=ParametrizedString('fields_on_entry_{@row_id}')) on_exit = Input( name=ParametrizedString('fields_on_exit_{@row_id}')) on_error = Input( name=ParametrizedString('fields_on_error_{@row_id}')) max_retries = Input( name=ParametrizedString('fields_max_retries_{@row_id}')) max_time = Input( name=ParametrizedString('fields_max_time_{@row_id}')) def delete(self): xpath = './/a[@title="Click to delete this field from schema"]' self.browser.click(xpath, parent=self) try: del self.row_id except AttributeError: pass @classmethod def all(cls, browser): result = [] for e in browser.elements(cls.ALL_FIELDS): result.append((browser.get_attribute('value', e), )) return result add_field = Text( '//div[@id="class_fields_div"]//i[contains(@class, "fa-plus")]') name = Input(name='field_name') type = BootstrapSelect('field_aetype') data_type = BootstrapSelect('field_datatype') default_value = Input(name='field_default_value') display_name = Input(name='field_display_name') description = Input(name='field_description') substitute = Checkbox(name='field_substitute') collect = Input(name='field_collect') message = Input(name='field_message') on_entry = Input(name='field_on_entry') on_exit = Input(name='field_on_exit') on_error = Input(name='field_on_error') max_retries = Input(name='field_max_retries') max_time = Input(name='field_max_time') finish_add_field = Text('//a[@title="Add this entry"]') save_button = Button('Save') reset_button = Button('Reset') cancel_button = Button('Cancel')
class FloatingIpDetailsToolBar(View): """ Represents toolbar of summary of port """ policy = Dropdown(text='Policy') download = Button(title='Print or export summary')
class ExpressionEditor(View, Pretty): """This class enables to embed the expression in a Form. Args: show_func: Function to call to show the expression if there are more of them. """ @View.nested class field_form_view(View): # noqa type = BootstrapSelect("chosen_typ") field = BootstrapSelect("chosen_field") key = BootstrapSelect("chosen_key") value = Input(name="chosen_value") user_input = Input(name="user_input") @View.nested class field_date_form(View): # noqa dropdown_select = BootstrapSelect("chosen_from_1") input_select_date = Calendar(name="miq_date_1_0") input_select_time = BootstrapSelect("miq_time_1_0") @View.nested class count_form_view(View): # noqa type = BootstrapSelect("chosen_typ") count = BootstrapSelect("chosen_count") key = BootstrapSelect("chosen_key") value = Input(name="chosen_value") user_input = Input(name="user_input") @View.nested class tag_form_view(View): # noqa type = BootstrapSelect("chosen_typ") tag = BootstrapSelect("chosen_tag") value = BootstrapSelect("chosen_value") user_input = Input(name="user_input") @View.nested class find_form_view(View): # noqa type = BootstrapSelect("chosen_typ") field = BootstrapSelect("chosen_field") skey = BootstrapSelect("chosen_skey") value = Input(name="chosen_value") check = BootstrapSelect("chosen_check") cfield = BootstrapSelect("chosen_cfield") ckey = BootstrapSelect("chosen_ckey") cvalue = Input(name="chosen_cvalue") @View.nested class registry_form_view(View): # noqa type = BootstrapSelect("chosen_typ") key = Input(name="chosen_regkey") value = Input(name="chosen_regval") operation = BootstrapSelect("chosen_key") contents = Input(name="chosen_value") @View.nested class date_specific_form_view(View): # noqa date = Calendar(name="miq_date_1_0") time = BootstrapSelect("miq_time_1_0") @View.nested class date_relative_form_view(View): # noqa from_ = BootstrapSelect("chosen_from_1") through = BootstrapSelect("chosen_through_1") ROOT = "//div[@id='exp_editor_div']" MAKE_BUTTON = "//span[not(contains(@style,'none'))]//img[@alt='{}']" ATOM_ROOT = "./div[@id='exp_atom_editor_div']" EXPRESSIONS_ROOT = "./fieldset/div" COMMIT = VersionPick({ Version.lowest(): "//img[@alt='Commit expression element changes']", "5.7.1": Button(title="Commit expression element changes"), }) DISCARD = VersionPick({ Version.lowest(): "//img[@alt='Discard expression element changes']", "5.7.1": Button(title="Discard expression element changes"), }) REMOVE = VersionPick({ Version.lowest(): ("//span[not(contains(@style, 'none'))]/" "/img[@alt='Remove this expression element']"), "5.8": Button(title="Remove this expression element"), }) NOT = VersionPick({ Version.lowest(): ("//span[not(contains(@style, 'none'))]" "//img[@alt='Wrap this expression element with a NOT']"), "5.8": Button(title="Wrap this expression element with a NOT"), }) OR = VersionPick({ Version.lowest(): ("//span[not(contains(@style, 'none'))]/" "/img[@alt='OR with a new expression element']"), "5.8": Button(title="OR with a new expression element"), }) AND = VersionPick({ Version.lowest(): ("//span[not(contains(@style, 'none'))]/" "/img[@alt='AND with a new expression element']"), "5.8": Button(title="AND with a new expression element"), }) REDO = VersionPick({ Version.lowest(): "//img[@alt='Redo']", "5.8": Button(title="Redo the last change"), }) UNDO = VersionPick({ Version.lowest(): "//img[@alt='Undo']", "5.8": Button(title="Undo the last change"), }) SELECT_SPECIFIC = "//img[@alt='Click to change to a specific Date/Time format']" SELECT_RELATIVE = "//img[@alt='Click to change to a relative Date/Time format']" pretty_attrs = ['show_loc'] def __init__(self, parent, show_loc=None, logger=None): View.__init__(self, parent, logger=logger) self.show_loc = show_loc def __locator__(self): return self.ROOT def click_undo(self): self.browser.click(self.UNDO) def click_redo(self): self.browser.click(self.REDO) def click_and(self): self.browser.click(self.AND) def click_or(self): self.browser.click(self.OR) def click_not(self): self.browser.click(self.NOT) def click_remove(self): self.browser.click(self.REMOVE) def click_commit(self): self.browser.click(self.COMMIT) def click_discard(self): self.browser.click(self.DISCARD) def click_switch_to_relative(self): self.browser.click(self.SELECT_RELATIVE) def click_switch_to_specific(self): self.browser.click(self.SELECT_SPECIFIC) @property def _atom_root(self): return self.browser.element(self.ATOM_ROOT) @property def _expressions_root(self): return self.browser.element(self.EXPRESSIONS_ROOT) def select_first_expression(self): """There is always at least one (???), so no checking of bounds.""" self.browser.elements("//a[contains(@id,'exp_')]", parent=self._expressions_root)[0].click() def select_expression_by_text(self, text): self.browser.click( "//a[contains(@id,'exp_')][contains(normalize-space(text()),'{}')]" .format(text)) def no_expression_present(self): els = self.browser.elements("//a[contains(@id,'exp_')]", parent=self._expressions_root) if len(els) > 1: return False return els[0].text.strip() == "???" def any_expression_present(self): return not self.no_expression_present() def is_editing(self): try: self.browser.element( "//a[contains(@id,'exp_')][contains(normalize-space(text()),'???')]", parent=self._expressions_root) return True except NoSuchElementException: return False def delete_whole_expression(self): while self.any_expression_present(): self.select_first_expression() self.click_remove() def read(self): """Returns whole expression as represented visually.""" return self._expressions_root.text.encode("utf-8").strip() def enable_editor(self): try: el = self.browser.element(self.show_loc) wait_for(lambda: el.is_displayed, num_sec=2, delay=0.2) el.click() except (TimedOutError, NoSuchElementException): pass def fill(self, expression): if self.show_loc is not None: self.enable_editor() prog = create_program(expression, self) before = self._expressions_root.text.encode("utf-8").strip() prog() after = self._expressions_root.text.encode("utf-8").strip() return before != after def fill_count(self, count=None, key=None, value=None): """ Fills the 'Count of' type of form. If the value is unspecified and we are in the advanced search form (user input), the user_input checkbox will be checked if the value is None. Args: count: Name of the field to compare (Host.VMs, ...). key: Operation to do (=, <, >=, ...). value: Value to check against. """ view = self.count_form_view view.fill(dict(type="Count of", count=count, key=key, value=value)) # In case of advanced search box if view.user_input.is_displayed: user_input = value is None view.user_input.fill(user_input) self.click_commit() def fill_tag(self, tag=None, value=None): """ Fills the 'Tag' type of form. Args: tag: Name of the field to compare. value: Value to check against. """ view = self.tag_form_view view.fill(dict(type="Tag", tag=tag, value=value)) # In case of advanced search box if view.user_input.is_displayed: user_input = value is None view.user_input.fill(user_input) self.click_commit() def fill_registry(self, key=None, value=None, operation=None, contents=None): """ Fills the 'Registry' type of form.""" view = self.registry_form_view view.fill( dict( type="Registry", key=key, value=value, operation=operation, contents=contents, )) self.click_commit() def fill_find(self, field=None, skey=None, value=None, check=None, cfield=None, ckey=None, cvalue=None): view = self.find_form_view view.fill( dict(type="Find", field=field, skey=skey, value=value, check=check, cfield=cfield, ckey=ckey, cvalue=cvalue)) self.click_commit() def fill_field(self, field=None, key=None, value=None): """ Fills the 'Field' type of form. Args: tag: Name of the field to compare (Host.VMs, ...). key: Operation to do (=, <, >=, IS NULL, ...). value: Value to check against. """ field_norm = field.strip().lower() if ("date updated" in field_norm or "date created" in field_norm or "boot time" in field_norm or "timestamp" in field_norm): no_date = False else: no_date = True view = self.field_form_view view.fill( dict( type="Field", field=field, key=key, value=value if no_date else None, )) # In case of advanced search box if view.user_input.is_displayed: user_input = value is None view.user_input.fill(user_input) if not no_date: # Flip the right part of form view = self.field_date_form if (isinstance(value, basestring) and not re.match(r"^[0-9]{2}/[0-9]{2}/[0-9]{4}$", value)): if not view.dropdown_select.is_displayed: self.click_switch_to_relative() view.fill({"dropdown_select": value}) self.click_commit() else: # Specific selection if not view.input_select_date.is_displayed: self.click_switch_to_specific() if (isinstance(value, tuple) or isinstance(value, list)) and len(value) == 2: date, time = value elif isinstance(value, basestring): # is in correct format mm/dd/yyyy # Date only (for now) date = value[:] time = None else: raise TypeError( "fill_field expects a 2-tuple (date, time) or string with date" ) # TODO datetime.datetime support view.input_select_date.fill(date) # Try waiting a little bit for time field # If we don't wait, committing the expression will glitch try: wait_for(lambda: view.input_select_time.is_displayed, num_sec=6) # It appeared, so if the time is to be set, we will set it # (passing None glitches) if time: view.input_select_time.fill(time) except TimedOutError: # Did not appear, ignore that pass finally: # And finally, commit the expression :) self.click_commit() else: self.click_commit()
class ReportsMultiBoxSelect(MultiBoxSelect): move_into_button = Button(title=Parameter("@move_into")) move_from_button = Button(title=Parameter("@move_from"))
class DashboardView(BaseLoggedInPage): """View that represents the Intelligence/Dashboard.""" reset_button = Button(title="Reset Dashboard Widgets to the defaults") def reset_widgets(self, cancel=False): """Clicks the reset button to reset widgets and handles the alert.""" self.browser.click(self.reset_button, ignore_ajax=True) self.browser.handle_alert(cancel=cancel, wait=10.0) self.browser.plugin.ensure_page_safe() add_widget = Dropdown('Add a widget') @View.nested class zoomed(View): # noqa """Represents the zoomed modal panel""" title = Text('.//div[@id="lightbox-panel"]//h2[contains(@class, "card-pf-title")]') close = Text('.//div[@id="lightbox-panel"]//a[normalize-space(@title)="Close"]') def ensure_zoom_closed(self): if self.zoomed.title.is_displayed: self.zoomed.close.click() @ParametrizedView.nested class dashboards(Tab, ParametrizedView): # noqa PARAMETERS = ('title', ) ALL_LOCATOR = './/ul[contains(@class, "nav-tabs-pf")]/li/a' COLUMN_LOCATOR = '//div[@id="col{}"]//h2' tab_name = Parameter('title') @classmethod def all(cls, browser): return [(browser.text(e), ) for e in browser.elements(cls.ALL_LOCATOR)] def column_widget_names(self, column_index): """Returns names of widgets in column specified. Args: column_index: Position of the column. Numbered from 1! Returns: :py:class:`list` of :py:class:`str` """ return [ self.browser.text(e) for e in self.browser.elements(self.COLUMN_LOCATOR.format(column_index))] @ParametrizedView.nested class widgets(ParametrizedView): # noqa PARAMETERS = ('title', ) ALL_LOCATOR = '//div[starts-with(@id, "w_")]//h2[contains(@class, "card-pf-title")]' BLANK_SLATE = './/div[contains(@class, "blank-slate-pf")]//h1' CHART = './div/div/div[starts-with(@id, "miq_widgetchart_")]' RSS = './div/div[contains(@class, "rss_widget")]' RSS_TABLE = './div[./div[contains(@class, "rss_widget")]]/div/table' TABLE = './div/table|./div/div/table' MC = ( './div/div[contains(@class, "mc")]/*[1]|./div/div[starts-with(@id, "dd_w") ' 'and contains(@id, "_box")]/*[1]') ROOT = ParametrizedLocator( './/div[starts-with(@id, "w_") and .//h2[contains(@class, "card-pf-title")' ' and normalize-space(.)={title|quote}]]') title = Text('.//h2[contains(@class, "card-pf-title")]') menu = Kebab(button_id=ParametrizedString('btn_{@widget_id}')) contents = ConditionalSwitchableView(reference='content_type') # Unsupported reading yet contents.register(None, default=True, widget=Widget()) contents.register('chart', widget=Widget()) # Reading supported contents.register('table', widget=Table(TABLE)) contents.register('rss', widget=Table(RSS_TABLE)) footer = Text('.//div[contains(@class, "card-pf-footer")]') @property def column(self): """Returns the column position of this widget. Numbered from 1!""" parent = self.browser.element('..') try: parent_id = self.browser.get_attribute('id', parent).strip() return int(re.sub(r'^col(\d+)$', '\\1', parent_id)) except (ValueError, TypeError, AttributeError): raise ValueError('Could not get the column index of widget') @property def minimized(self): return not self.browser.is_displayed(self.MC) @cached_property def widget_id(self): id_attr = self.browser.get_attribute('id', self) return int(id_attr.rsplit('_', 1)[-1]) @cached_property def content_type(self): if self.browser.elements(self.BLANK_SLATE): # No data yet return None elif self.browser.elements(self.RSS): return 'rss' elif self.browser.is_displayed(self.CHART): return 'chart' elif self.browser.is_displayed(self.TABLE): return 'table' else: return None @property def blank(self): return bool(self.browser.elements(self.BLANK_SLATE)) @classmethod def all(cls, browser): return [(browser.text(e), ) for e in browser.elements(cls.ALL_LOCATOR)] @property def is_displayed(self): return ( self.logged_in_as_current_user and self.navigation.currently_selected == ['Cloud Intel', 'Dashboard'])
class KeyPairDetailsToolbar(View): policy = Dropdown('Policy') configuration = Dropdown('Configuration') download = Button(title='Download summary in PDF format')
class KeyPairAddForm(View): name = TextInput(id='name') public_key = TextInput(id='public_key') provider = BootstrapSelect(id='ems_id') add = Button('Add') cancel = Button('Cancel')
class ServerCollectLogsToolbar(View): edit = Button(title="Edit the Log Depot settings for the selected Server") collect = Dropdown('Collect Logs')