def create(self, name, provider, public_key=None, cancel=False): """Create new keyPair. Args: name (str): name of the KeyPair public_key (str): RSA Key if present provider (str): Cloud Provider cancel (boolean): Cancel Keypair creation """ view = navigate_to(self, 'Add') changed = view.form.fill({ 'name': name, 'public_key': public_key, 'provider': provider.name }) if cancel and not changed: view.form.cancel.click() flash_message = 'Add of new Key Pair was cancelled by the user' else: view.form.add.click() flash_message = VersionPick({ Version.lowest(): 'Creating Key Pair {}'.format(name), '5.8': 'Key Pair "{}" created'.format(name) }).pick(self.appliance.version) # add/cancel should redirect, new view view = self.create_view(KeyPairAllView) # TODO BZ 1444520 causing ridiculous redirection times after submitting the form wait_for(lambda: view.is_displayed, num_sec=240, delay=2, fail_func=view.flush_widget_cache, handle_exception=True) assert view.is_displayed view.entities.flash.assert_success_message(flash_message) keypair = self.instantiate(name, provider, public_key=public_key) return keypair
class ProvidersManagePoliciesView(BaseLoggedInPage): """ Provider's Manage Policies view """ policies = VersionPick({ Version.lowest(): DynaTree('protect_treebox'), '5.7': BootstrapTreeview('protectbox') }) @View.nested class entities(BaseNonInteractiveEntitiesView): # noqa @property def entity_class(self): return ProviderEntity().pick(self.browser.product_version) save = Button('Save') reset = Button('Reset') cancel = Button('Cancel') @property def is_displayed(self): return False
def pytest_generate_tests(metafunc): """ Build a list of tuples containing (group_name, context) Returns: tuple containing (group_name, context) where group_name is a string and context is ViaUI/SSUI """ appliance = find_appliance(metafunc) parameter_list = [] id_list = [] # TODO: Include SSUI role_access dict and VIASSUI context role_access_ui = VersionPick({ Version.lowest(): role_access_ui_58z, '5.9': role_access_ui_59z, '5.10': role_access_ui_510z }).pick(appliance.version) logger.info('Using the role access dict: %s', role_access_ui) roles_and_context = [(role_access_ui, ViaUI)] for role_access, context in roles_and_context: for group in role_access.keys(): parameter_list.append((group, role_access, context)) id_list.append('{}-{}'.format(group, context)) metafunc.parametrize('group_name, role_access, context', parameter_list)
class AlertProfilesEditAssignmentsView(ControlExplorerView): title = Text("#explorer_title_text") assign_to = BootstrapSelect("chosen_assign_to") tag_category = BootstrapSelect("chosen_cat") selections = CbTree( VersionPick({ Version.lowest(): "obj_treebox", "5.9": "object_treebox" })) header = Text("//div[@id='alert_profile_assign_div']/h3") based_on = Text('//label[normalize-space(.)="Based On"]/../div') save_button = Button("Save") reset_button = Button("Reset") cancel_button = Button("Cancel") @property def is_displayed(self): return (self.in_control_explorer and self.title.text == 'Alert Profile "{}"'.format( self.context["object"].description) and self.header.text == "Assignments" and self.based_on == self.context["object"].TYPE)
class EditGroupSequenceView(ConfigurationView): """ Edit Groups Sequence View in CFME UI """ group_order_selector = VersionPick({ Version.lowest(): UpDownSelect( '#seq_fields', './/a[@title="Move selected fields up"]/img', './/a[@title="Move selected fields down"]/img'), '5.8': UpDownSelect( '#seq_fields', '//button[@title="Move selected fields up"]/i', '//button[@title="Move selected fields down"]/i'), }) save_button = Button('Save') reset_button = Button('Reset') cancel_button = Button('Cancel') @property def is_displayed(self): return ( self.accordions.accesscontrol.is_opened and self.title.text == "Editing Sequence of User Groups" )
def create(self, cancel=False): """Create new keypair""" def refresh(): self.provider.refresh_provider_relationships() view.browser.selenium.refresh() view.flush_widget_cache() view = navigate_to(self, 'Add') view.form.fill({ 'name': self.name, 'public_key': self.public_key, 'provider': self.provider.name }) if cancel: view.form.cancel.click() flash_message = 'Add of new Key Pair was cancelled by the user' else: view.form.add.click() flash_message = VersionPick({ Version.lowest(): 'Creating Key Pair {}'.format(self.name), '5.8': 'Key Pair "{}" created'.format(self.name) }).pick(self.appliance.version) # add/cancel should redirect, new view view = self.create_view(KeyPairAllView) # TODO BZ 1444520 causing ridiculous redirection times after submitting the form wait_for(lambda: view.is_displayed, fail_condition=False, num_sec=120, delay=3, fail_func=lambda: view.flush_widget_cache(), handle_exception=True) view = self.create_view(KeyPairAllView) view.entities.flash.assert_no_error() view.entities.flash.assert_success_message(flash_message)
class MyServicesView(BaseLoggedInPage): def in_myservices(self): return (self.logged_in_as_current_user and self.navigation.currently_selected == ['Services', 'MyServices']) @property def is_displayed(self): return (self.in_myservices and self.configuration.is_displayed and not self.myservice.is_dimmed) @View.nested class myservice(Accordion): # noqa ACCORDION_NAME = "Services" tree = ManageIQTree() @View.nested class toolbar(View): # noqa """ represents provider toolbar and its controls """ configuration = Dropdown(text='Configuration') policy = Dropdown(text='Policy') lifecycle = Dropdown(text='Lifecycle') download = Dropdown(text='Download') view_selector = View.nested(ItemsToolBarViewSelector) # TODO drop '_btn' suffix reload = Button(title=VersionPick({ Version.lowest(): 'Reload current display', '5.9': 'Refresh this page' })) configuration = Dropdown('Configuration') policy_btn = Dropdown('Policy') lifecycle_btn = Dropdown('Lifecycle') download_choice = Dropdown('Download')
def delete(self, cancel=False, wait=False): view = navigate_to(self, 'Details') # TODO: get rid of this resolve when widgetastic.core/pull/68 is merged item_name = VersionPick({Version.lowest(): 'Remove this Key Pair', '5.9': 'Remove this Key Pair from Inventory'} ).pick(self.appliance.version) view.toolbar.configuration.item_select(item_name, handle_alert=(not cancel)) # cancel doesn't redirect, confirmation does view.flush_widget_cache() if cancel: view = self.create_view(KeyPairDetailsView) else: view = self.create_view(KeyPairAllView) wait_for(lambda: view.is_displayed, fail_condition=False, num_sec=10, delay=1) # flash message only displayed if it was deleted if not cancel: view.flash.assert_no_error() view.flash.assert_success_message('Delete initiated for 1 Key Pair') if wait: def refresh(): self.provider.refresh_provider_relationships() view.browser.refresh() view.flush_widget_cache() view = navigate_to(self.parent, 'All') wait_for( lambda: self.name in view.entities.all_entity_names, message="Wait keypairs to disappear", fail_condition=True, num_sec=300, delay=5, fail_func=refresh )
class ContainerAllView(ContainerObjectAllBaseView): """Containers All view""" summary = Text(VersionPick({ Version.lowest(): '//h3[normalize-space(.) = {}]'.format(quote('All Containers')), '5.8': '//h1[normalize-space(.) = {}]'.format(quote('Containers')) })) containers = Table(locator="//div[@id='list_grid']//table") @View.nested class Filters(Accordion): # noqa ACCORDION_NAME = "Filters" @View.nested class Navigation(VerticalNavigation): DIV_LINKS_MATCHING = './/div/ul/li/a[contains(text(), {txt})]' def __init__(self, parent, logger=None): VerticalNavigation.__init__(self, parent, '#Container_def_searches', logger=logger) tree = ManageIQTree() @property def is_displayed(self): return self.summary.is_displayed
class retirement(Tab): # noqa # TODO Somehow need to handle a modal window copy_from_provisioning = Button("Copy from provisioning") repository = BootstrapSelect("retirement_repository_id") playbook = BootstrapSelect("retirement_playbook_id") machine_credential = BootstrapSelect( "retirement_machine_credential_id") cloud_type = BootstrapSelect("retirement_cloud_type") cloud_credential = BootstrapSelect("retirement_cloud_credential_id") localhost = Input(id="retirement_inventory_localhost") specify_host_values = Input(id="retirement_inventory_specify") hosts = Input("retirement_inventory") logging_output = BootstrapSelect("retirement_log_output") max_ttl = Input("retirement_execution_ttl") escalate_privilege = BootstrapSwitch("retirement_become_enabled") verbosity = BootstrapSelect("retirement_verbosity") remove_resources = VersionPick({ Version.lowest(): BootstrapSelect("vm.catalogItemModel.retirement_remove_resources"), "5.9": BootstrapSelect( "vm.vm.catalogItemModel.retirement_remove_resources") }) extra_vars = AnsibleExtraVariables(tab="retirement")
class ImageRegistryDetailsView(ContainerObjectDetailsBaseView): """Container Image Registries Detail view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Image Registries', '5.9': 'Container Image Registries' })
class ImageRegistryAllView(ContainerObjectAllBaseView): """Container Images Registries All view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Image Registries', '5.9': 'Container Image Registries' })
class PodAllView(ContainerObjectAllBaseView): """Container Pods All view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Pods', '5.9': 'Container Pods' })
class RouteAllView(ContainerObjectAllBaseView): """Container Routes All view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Routes', '5.9': 'Container Routes' })
class OrderForm(ServicesCatalogView): """Represents the order form of a service. This form doesn't have a static set of elements apart from titles and buttons. In the most cases the fields can be either regular inputs or dropdowns. Their locators depend on field names. In order to find and fill required fields a parametrized view is used here. The keys of a fill dictionary should match ids of the fields. For instance there is a field with such html <input id="some_key"></input>, so a fill dictionary should look like that: {"some_key": "some_value"} """ title = Text('#explorer_title_text') dialog_title = Text( VersionPick({ Version.lowest(): ".//div[@id='main_div']//h3", "5.9": ".//div[@id='main_div']//h2" }) ) @ParametrizedView.nested class fields(ParametrizedView): # noqa PARAMETERS = ("key",) input = Input(id=Parameter("key")) select = Select(id=Parameter("key")) param_input = Input(id=ParametrizedString("param_{key}")) dropdown = VersionPick({ Version.lowest(): BootstrapSelect(Parameter("key")), "5.9": BootstrapSelect(locator=ParametrizedString( ".//div[contains(@class, 'bootstrap-select') and " "select[@id={key|quote}]]")) }) param_dropdown = VersionPick({ Version.lowest(): BootstrapSelect(ParametrizedString("param_{key}")), "5.9": BootstrapSelect(locator=ParametrizedString( ".//div[contains(@class, 'bootstrap-select') and " "select[@id='param_{key}']]")) }) @property def visible_widget(self): if self.input.is_displayed: return self.input elif self.dropdown.is_displayed: return self.dropdown elif self.param_input.is_displayed: return self.param_input elif self.param_dropdown.is_displayed: return self.param_dropdown elif self.select.is_displayed: return self.select else: raise ItemNotFound("Visible widget is not found") def read(self): return self.visible_widget.read() def fill(self, value): return self.visible_widget.fill(value) def fill(self, fill_data): values = deflatten_dict(fill_data) was_change = False self.before_fill(values) for key, value in values.items(): widget = self.fields(key) if value is None: self.logger.debug('Skipping fill of %r because value was None', key) continue try: if widget.fill(value): was_change = True except NotImplementedError: continue self.after_fill(was_change) return was_change
def step(self): self.prerequisite_view.toolbar.configuration.item_select( VersionPick({ Version.lowest(): 'Add Existing Containers Provider', '5.9': 'Add a new Containers Provider' }).pick(self.obj.appliance.version))
class ProjectDetailsView(ContainerObjectDetailsBaseView): """Container Projects Detail view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Projects', '5.9': 'Container Projects' })
def step(self, *args, **kwargs): navigation_path = VersionPick(self.obj.navigation_path).pick( self.obj.appliance.version) self.prerequisite_view.navigation.select(*navigation_path)
def in_manager(self): navigation_path = VersionPick( self.context['object'].navigation_path).pick( self.context['object'].appliance.version) return (self.logged_in_as_current_user and self.navigation.currently_selected == navigation_path)
def view_classes(self): return VersionPick({ Version.lowest(): RetirementView, "5.9": RetirementViewWithOffset })
def container_provider_edit_view_class(self): return VersionPick({ Version.lowest(): ContainerProviderEditView, '5.9': ContainerProviderEditViewUpdated })
class mytasks(Tab): # noqa TAB_NAME = VersionPick({ Version.lowest(): 'My VM and Container Analysis Tasks', '5.9': 'My Tasks' }) table = Table(table_loc)
class TestForm(View): table = Table('#withwidgets', column_widgets={ 'Column 2': TextInput(locator='./input'), 'Column 3': VersionPick({ Version.lowest(): TextInput(locator='./input'), '2.0': None})})
def DeploymentRoleEntity(): # noqa """Temporary wrapper for Deployment Role Entity during transition to JS based Entity """ return VersionPick({ Version.lowest(): NonJSDepRoleEntity, '5.9': JSBaseEntity, })
class LoginPage(View): flash = FlashMessages( VersionPick({ Version.lowest(): 'div#flash_text_div', '5.8': '//div[@class="flash_text_div"]' }) ) class details(View): # noqa region = Text('.//p[normalize-space(text())="Region:"]/span') zone = Text('.//p[normalize-space(text())="Zone:"]/span') appliance = Text('.//p[normalize-space(text())="Appliance:"]/span') change_password = Text('.//a[normalize-space(.)="Update password"]') back = Text('.//a[normalize-space(.)="Back"]') username = Input(name='user_name') password = Input(name='user_password') new_password = Input(name='user_new_password') verify_password = Input(name='user_verify_password') login = Button(id='login') def show_update_password(self): if not self.new_password.is_displayed: self.change_password.click() def hide_update_password(self): if self.new_password.is_displayed: self.back.click() def login_admin(self, **kwargs): username = conf.credentials['default']['username'] password = conf.credentials['default']['password'] cred = Credential(principal=username, secret=password) from cfme.configure.access_control import User user = User(credential=cred, name='Administrator') return self.log_in(user, **kwargs) def submit_login(self, method='click_on_login'): if method == 'click_on_login': self.login.click() elif method == 'press_enter_after_password': self.browser.send_keys(Keys.ENTER, self.password) elif method == '_js_auth_fn': self.browser.execute_script('miqAjaxAuth();') else: raise ValueError('Unknown method {}'.format(method)) if self.flash.is_displayed: self.flash.assert_no_error() def log_in(self, user, method='click_on_login'): self.fill({ 'username': user.credential.principal, 'password': user.credential.secret, }) self.submit_login(method) logged_in_view = self.browser.create_view(BaseLoggedInPage) if logged_in_view.logged_in: if user.name is None: name = logged_in_view.current_fullname self.logger.info( 'setting the appliance.user.name to %r because it was not specified', name) user.name = name self.extra.appliance.user = user def update_password( self, username, password, new_password, verify_password=None, method='click_on_login'): self.show_update_password() self.fill({ 'username': username, 'password': password, 'new_password': new_password, 'verify_password': verify_password if verify_password is not None else new_password }) self.submit_login(method) def logged_in_as_user(self, user): return False @property def logged_in_as_current_user(self): return False @property def current_username(self): return None @property def current_fullname(self): return None @property def logged_in(self): return not self.logged_out @property def logged_out(self): return self.username.is_displayed and self.password.is_displayed and self.login.is_displayed @property def is_displayed(self): return self.logged_out
class ButtonGroup(Updateable, Navigatable): """Create,Edit and Delete Button Groups Args: text: The button Group name. hover: The button group hover text. type: The object type. """ CLUSTER = "Cluster" DATASTORE = "Datastore" HOST = VersionPick({version.LOWEST: "Host", '5.4': "Host / Node"}) PROVIDER = "Provider" SERVICE = "Service" TEMPLATE = "VM Template and Image" VM_INSTANCE = "VM and Instance" def __init__(self, text=None, hover=None, type=None, appliance=None): Navigatable.__init__(self, appliance=appliance) self.text = text self.hover = hover self.type = type def create(self): view = navigate_to(self, 'Add') view.fill({ 'text': self.text, 'hover': self.hover, 'image': 'Button Image 1', }) view.add_button.click() view = self.create_view(ButtonGroupObjectTypeView) assert view.is_displayed view.flash.assert_no_error() view.flash.assert_message('Buttons Group "{}" was added'.format( self.hover)) def update(self, updates): view = navigate_to(self, 'Edit') changed = view.fill(updates) if changed: view.save_button.click() else: view.cancel_button.click() view = self.create_view(ButtonGroupDetailView, override=updates) assert view.is_displayed view.flash.assert_no_error() if changed: view.flash.assert_message('Buttons Group "{}" was saved'.format( updates.get('hover', self.hover))) else: view.flash.assert_message( 'Edit of Buttons Group "{}" was cancelled by the user'.format( self.text)) def delete(self, cancel=False): view = navigate_to(self, 'Details') view.configuration.item_select('Remove this Button Group', handle_alert=not cancel) if cancel: assert view.is_displayed view.flash.assert_no_error() else: view = self.create_view(ButtonGroupObjectTypeView) assert view.is_displayed view.flash.assert_no_error() view.flash.assert_message( 'Buttons Group "{}": Delete successful'.format(self.hover)) @property def exists(self): try: navigate_to(self, 'Details') return True except CandidateNotFound: return False def delete_if_exists(self): if self.exists: self.delete()
class RouteDetailsView(ContainerObjectDetailsBaseView): """Container Routes Detail view""" SUMMARY_TEXT = VersionPick({ Version.lowest(): 'Routes', '5.9': 'Container Routes' })
class myothertasks(Tab): # noqa TAB_NAME = VersionPick({ '5.9': 'My Tasks', Version.lowest(): 'My Other UI Tasks' }) table = Table(table_loc)
def DatastoreEntity(): # noqa """Temporary wrapper for Datastore Entity during transition to JS based Entity """ return VersionPick({ Version.lowest(): NonJSDatastoreEntity, '5.9': JSDatastoreEntity, })
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. Returns: See :py:func:`cfme.web_ui.fill`. """ 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. Returns: See :py:func:`cfme.web_ui.fill`. """ 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()