def send_keys(loc, text): """Sends the supplied keys to an element. Handles the file upload fields on background. If it detects the element is and input of type file, it uses the LocalFileDetector so the file gets transferred properly. Otherwise it takes care of having UselessFileDetector. Args: loc: A locator, expects either a string, WebElement, tuple. text: The text to inject into the element. """ if text is not None: file_intercept = False # If the element is input type file, we will need to use the file detector if tag(loc) == 'input': type_attr = get_attribute(loc, 'type') if type_attr and type_attr.strip() == 'file': file_intercept = True try: if file_intercept: # If we detected a file upload field, let's use the file detector. browser().file_detector = LocalFileDetector() move_to_element(loc).send_keys(text) finally: # Always the UselessFileDetector for all other kinds of fields, so do not leave # the LocalFileDetector there. if file_intercept: browser().file_detector = UselessFileDetector() wait_for_ajax()
def generated_request(appliance, infra_provider, provider_data, provisioning, template_name, vm_name): """Creates a provision request, that is not automatically approved, and returns the search data. After finishing the test, request should be automatically deleted. Slightly modified code from :py:module:`cfme.tests.infrastructure.test_provisioning` """ first_name = fauxfactory.gen_alphanumeric() last_name = fauxfactory.gen_alphanumeric() notes = fauxfactory.gen_alphanumeric() e_mail = "{}@{}.test".format(first_name, last_name) host, datastore = map(provisioning.get, ('host', 'datastore')) vm = Vm(name=vm_name, provider=infra_provider, template_name=template_name) navigate_to(vm, 'ProvisionVM') provisioning_data = { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes, 'vm_name': vm_name, 'host_name': { 'name': [host] }, 'datastore_name': { 'name': [datastore] }, 'num_vms': "10", # so it won't get auto-approved } # Same thing, different names. :\ if provider_data["type"] == 'rhevm': provisioning_data['provision_type'] = 'Native Clone' elif provider_data["type"] == 'virtualcenter': provisioning_data['provision_type'] = 'VMware' try: provisioning_data['vlan'] = provisioning['vlan'] except KeyError: # provisioning['vlan'] is required for rhevm provisioning if provider_data["type"] == 'rhevm': raise pytest.fail( 'rhevm requires a vlan value in provisioning info') fill(provisioning_form, provisioning_data, action=provisioning_form.submit_button) flash.assert_no_errors() request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } yield request_cells browser().get(store.base_url) appliance.server.login_admin() requests.delete_request(request_cells) flash.assert_no_errors()
def navigate_accordions(accordions, page_name, ui_bench_pg_limit, ui_worker_pid, prod_tail, soft_assert): pages = [] for acc_tree in accordions: pages.extend( analyze_page_stat( perf_click(ui_worker_pid, prod_tail, True, accordion.click, acc_tree), soft_assert)) logger.info('Starting to read tree: {}'.format(acc_tree)) tree_contents, sel_time = perf_bench_read_tree( accordion.tree(acc_tree)) logger.info('{} tree read in {}ms'.format(acc_tree, sel_time)) pages.extend( analyze_page_stat( perf_click(ui_worker_pid, prod_tail, False, None), soft_assert)) nav_limit = 0 count = -1 if accordions[acc_tree] in ui_bench_pg_limit: nav_limit = ui_bench_pg_limit[accordions[acc_tree]] count = 0 paths = [] generate_tree_paths(tree_contents, [], paths) logger.info('Found {} tree paths'.format(len(paths))) for path in paths: logger.info('Navigating to: {}, {}'.format(acc_tree, path[-1])) try: pages.extend( analyze_page_stat( perf_click(ui_worker_pid, prod_tail, True, accordion.tree(acc_tree).click_path, *path), soft_assert)) count += 1 # Navigate out of the page every 4th click if (count % 4) == 0: pages.extend( analyze_page_stat( perf_click(ui_worker_pid, prod_tail, False, sel.force_navigate, 'dashboard'), soft_assert)) pages.extend( analyze_page_stat( perf_click(ui_worker_pid, prod_tail, False, sel.force_navigate, page_name), soft_assert)) except CandidateNotFound: logger.info('Could not navigate to: '.format(path[-1])) except UnexpectedAlertPresentException: logger.warning( 'UnexpectedAlertPresentException - page_name: {}, accordion: {},' ' path: {}'.format(page_name, acc_tree, path[-1])) browser().switch_to_alert().dismiss() if not nav_limit == 0 and count >= nav_limit: break return pages
def check_logged_out(): if browser.browser() is not None: browser.quit() browser.ensure_browser_open() login.logout() yield if browser.browser() is not None: browser.quit() browser.ensure_browser_open() login.logout()
def generated_request(provider, provider_data, provisioning, template_name, vm_name): """Creates a provision request, that is not automatically approved, and returns the search data. After finishing the test, request should be automatically deleted. Slightly modified code from :py:module:`cfme.tests.infrastructure.test_provisioning` """ first_name = fauxfactory.gen_alphanumeric() last_name = fauxfactory.gen_alphanumeric() notes = fauxfactory.gen_alphanumeric() e_mail = "{}@{}.test".format(first_name, last_name) host, datastore = map(provisioning.get, ('host', 'datastore')) pytest.sel.force_navigate('infrastructure_provision_vms', context={ 'provider': provider, 'template_name': template_name, }) provisioning_data = { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes, 'vm_name': vm_name, 'host_name': {'name': [host]}, 'datastore_name': {'name': [datastore]}, 'num_vms': "10", # so it won't get auto-approved } # Same thing, different names. :\ if provider_data["type"] == 'rhevm': provisioning_data['provision_type'] = 'Native Clone' elif provider_data["type"] == 'virtualcenter': provisioning_data['provision_type'] = 'VMware' try: provisioning_data['vlan'] = provisioning['vlan'] except KeyError: # provisioning['vlan'] is required for rhevm provisioning if provider_data["type"] == 'rhevm': raise pytest.fail('rhevm requires a vlan value in provisioning info') provisioning_form.fill(provisioning_data) pytest.sel.click(provisioning_form.submit_button) flash.assert_no_errors() request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } yield request_cells browser().get(store.base_url) login_admin() requests.delete_request(request_cells) flash.assert_no_errors()
def really_logout(): """A convenience function logging out This function simply ensures that we are logged out and that a new browser is loaded ready for use. """ try: current_appliance.server.logout() except AttributeError: try: browser().quit() except AttributeError: ensure_browser_open()
def really_logout(): """A convenience function logging out This function simply ensures that we are logged out and that a new browser is loaded ready for use. """ try: logout() except AttributeError: try: browser().quit() except AttributeError: ensure_browser_open()
def generated_request(provider, provider_data, provisioning, template_name, vm_name): """Creates a provision request, that is not automatically approved, and returns the search data. After finishing the test, request should be automatically deleted. Slightly modified code from :py:module:`cfme.tests.infrastructure.test_provisioning` """ first_name = fauxfactory.gen_alphanumeric() last_name = fauxfactory.gen_alphanumeric() notes = fauxfactory.gen_alphanumeric() e_mail = "{}@{}.test".format(first_name, last_name) host, datastore = map(provisioning.get, ("host", "datastore")) vm = Vm(name=vm_name, provider=provider, template_name=template_name) navigate_to(vm, "ProvisionVM") provisioning_data = { "email": e_mail, "first_name": first_name, "last_name": last_name, "notes": notes, "vm_name": vm_name, "host_name": {"name": [host]}, "datastore_name": {"name": [datastore]}, "num_vms": "10", # so it won't get auto-approved } # Same thing, different names. :\ if provider_data["type"] == "rhevm": provisioning_data["provision_type"] = "Native Clone" elif provider_data["type"] == "virtualcenter": provisioning_data["provision_type"] = "VMware" try: provisioning_data["vlan"] = provisioning["vlan"] except KeyError: # provisioning['vlan'] is required for rhevm provisioning if provider_data["type"] == "rhevm": raise pytest.fail("rhevm requires a vlan value in provisioning info") fill(provisioning_form, provisioning_data, action=provisioning_form.submit_button) flash.assert_no_errors() request_cells = {"Description": "Provision from [{}] to [{}###]".format(template_name, vm_name)} yield request_cells browser().get(store.base_url) login_admin() requests.delete_request(request_cells) flash.assert_no_errors()
def move_to_element(loc, **kwargs): """ Moves to an element. Args: loc: A locator, expects either a string, WebElement, tuple. Returns: It passes `loc` through to make it possible to use in case we want to immediately use the element that it is being moved to. """ brand = "//div[@id='page_header_div']//div[contains(@class, 'brand')]" wait_for_ajax() el = element(loc, **kwargs) move_to = ActionChains(browser()).move_to_element(el) try: move_to.perform() except MoveTargetOutOfBoundsException: # ff workaround execute_script("arguments[0].scrollIntoView();", el) if elements(brand) and not is_displayed(brand): # If it does it badly that it moves whole page, this moves it back try: execute_script("arguments[0].scrollIntoView();", element(brand)) except MoveTargetOutOfBoundsException: pass move_to.perform() return el
def current_url(): """ Returns the current_url of the page Returns: A url. """ return browser().current_url
def move_to_element(loc, **kwargs): """ Moves to an element. Args: loc: A locator, expects either a string, WebElement, tuple. Returns: Returns the element it was moved to to enable chaining. """ brand = "//div[@id='page_header_div']//div[contains(@class, 'brand')]" wait_for_ajax() el = element(loc, **kwargs) if el.tag_name == "option": # Instead of option, let's move on its parent <select> if possible parent = element("..", root=el) if parent.tag_name == "select": move_to_element(parent) return el move_to = ActionChains(browser()).move_to_element(el) try: move_to.perform() except MoveTargetOutOfBoundsException: # ff workaround execute_script("arguments[0].scrollIntoView();", el) if elements(brand) and not is_displayed(brand): # If it does it badly that it moves whole page, this moves it back try: execute_script("arguments[0].scrollIntoView();", element(brand)) except MoveTargetOutOfBoundsException: pass try: move_to.perform() except MoveTargetOutOfBoundsException: # This has become desperate now. raise exceptions.CannotScrollException( "Despite all the workarounds, scrolling to `{}` was unsuccessful.".format(loc)) return el
def needs_firefox(): """ Fixture which skips the test if not run under firefox. I recommend putting it in the first place. """ if browser.browser().name != "firefox": pytest.skip(msg="This test needs firefox to run")
def click(loc, wait_ajax=True, no_custom_handler=False): """ Clicks on an element. If the element implements `_custom_click_handler` the control will be given to it. Then the handler decides what to do (eg. do not click under some circumstances). Args: loc: A locator, expects either a string, WebElement, tuple or an object implementing `_custom_click_handler` method. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) no_custom_handler: To prevent recursion, the custom handler sets this to True. """ if hasattr(loc, "_custom_click_handler") and not no_custom_handler: # Object can implement own modification of click behaviour return loc._custom_click_handler() # Move mouse cursor to element move_to_element(loc) # and then click on current mouse position ActionChains(browser()).click().perform() # -> using this approach, we don't check if we clicked a specific element if wait_ajax: wait_for_ajax()
def get_logging_url(self): def report_kibana_failure(): raise RuntimeError("Kibana not found in the window title or content") browser_instance = browser() all_windows_before = browser_instance.window_handles appliance_window = browser_instance.current_window_handle self.monitor.item_select('External Logging') all_windows_after = browser_instance.window_handles new_windows = set(all_windows_after) - set(all_windows_before) if not new_windows: raise RuntimeError("No logging window was open!") logging_window = new_windows.pop() browser_instance.switch_to_window(logging_window) logging_url = browser_instance.current_url wait_for(lambda: "kibana" in browser_instance.title.lower() + " " + browser_instance.page_source.lower(), fail_func=report_kibana_failure, num_sec=60, delay=5) browser_instance.close() browser_instance.switch_to_window(appliance_window) return logging_url
def move_to_element(loc, **kwargs): """ Moves to an element. Args: loc: A locator, expects either a string, WebElement, tuple. Returns: Returns the element it was moved to to enable chaining. """ brand = "//div[@id='page_header_div']//div[contains(@class, 'brand')]" wait_for_ajax() el = element(loc, **kwargs) move_to = ActionChains(browser()).move_to_element(el) try: move_to.perform() except MoveTargetOutOfBoundsException: # ff workaround execute_script("arguments[0].scrollIntoView();", el) if elements(brand) and not is_displayed(brand): # If it does it badly that it moves whole page, this moves it back try: execute_script("arguments[0].scrollIntoView();", element(brand)) except MoveTargetOutOfBoundsException: pass try: move_to.perform() except MoveTargetOutOfBoundsException: # This has become desperate now. raise exceptions.CannotScrollException( "Despite all the workarounds, scrolling to `{}` was unsuccessful.".format(loc)) return el
def get_logging_url(self): def report_kibana_failure(): raise RuntimeError( "Kibana not found in the window title or content") browser_instance = browser() all_windows_before = browser_instance.window_handles appliance_window = browser_instance.current_window_handle self.monitor.item_select('External Logging') all_windows_after = browser_instance.window_handles new_windows = set(all_windows_after) - set(all_windows_before) if not new_windows: raise RuntimeError("No logging window was open!") logging_window = new_windows.pop() browser_instance.switch_to_window(logging_window) logging_url = browser_instance.current_url wait_for(lambda: "kibana" in browser_instance.title.lower() + " " + browser_instance.page_source.lower(), fail_func=report_kibana_failure, num_sec=60, delay=5) browser_instance.close() browser_instance.switch_to_window(appliance_window) return logging_url
def get(url): """ Changes page to the spceified URL Args: url: URL to navigate to. """ return browser().get(url)
def move_to_element(loc): """ Moves to an element. Args: loc: A locator, expects either a string, WebElement, tuple. """ ActionChains(browser()).move_to_element(element(loc)).perform()
def handle_alert(cancel=False, wait=30.0, squash=False, prompt=None, check_present=False): """Handles an alert popup. Args: cancel: Whether or not to cancel the alert. Accepts the Alert (False) by default. wait: Time to wait for an alert to appear. Default 30 seconds, can be set to 0 to disable waiting. squash: Whether or not to squash errors during alert handling. Default False prompt: If the alert is a prompt, specify the keys to type in here check_present: Does not squash :py:class:`selenium.common.exceptions.NoAlertPresentException` Returns: True if the alert was handled, False if exceptions were squashed, None if there was no alert. No exceptions will be raised if ``squash`` is True and ``check_present`` is False. Raises: utils.wait.TimedOutError: If the alert popup does not appear selenium.common.exceptions.NoAlertPresentException: If no alert is present when accepting or dismissing the alert. """ # throws timeout exception if not found try: if wait: WebDriverWait(browser(), wait).until(expected_conditions.alert_is_present()) popup = get_alert() answer = 'cancel' if cancel else 'ok' t = "alert" if prompt is None else "prompt" logger.info('Handling %s %s, clicking %s', t, popup.text, answer) if prompt is not None: logger.info("Typing in: %s", prompt) popup.send_keys(prompt) popup.dismiss() if cancel else popup.accept() # Should any problematic "double" alerts appear here, we don't care, just blow'em away. dismiss_any_alerts() wait_for_ajax() return True except NoAlertPresentException: if check_present: raise else: return None except Exception as e: logger.exception(e) if squash: return False else: raise
def _t(t, root=None): """Assume tuple is a 2-item tuple like (By.ID, 'myid'). Handles the case when root= locator resolves to multiple elements. In that case all of them are processed and all results are put in the same list.""" result = [] for root_element in (elements(root) if root is not None else [browser()]): result += root_element.find_elements(*t) return result
def drag_and_drop(source_element, dest_element): """Drag and Drop element. Args: loc: A locator, expects either a string, WebElement, tuple. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) """ ActionChains(browser()).drag_and_drop(dest_element, source_element).perform()
def navigate_accordions(accordions, page_name, ui_bench_pg_limit, ui_worker_pid, prod_tail, soft_assert): pages = [] for acc_tree in accordions: pages.extend(analyze_page_stat(perf_click(ui_worker_pid, prod_tail, True, accordion.click, acc_tree), soft_assert)) logger.info('Starting to read tree: {}'.format(acc_tree)) tree_contents, sel_time = perf_bench_read_tree(accordion.tree(acc_tree)) logger.info('{} tree read in {}ms'.format(acc_tree, sel_time)) pages.extend(analyze_page_stat(perf_click(ui_worker_pid, prod_tail, False, None), soft_assert)) nav_limit = 0 count = -1 if accordions[acc_tree] in ui_bench_pg_limit: nav_limit = ui_bench_pg_limit[accordions[acc_tree]] count = 0 paths = [] generate_tree_paths(tree_contents, [], paths) logger.info('Found {} tree paths'.format(len(paths))) for path in paths: logger.info('Navigating to: {}, {}'.format(acc_tree, path[-1])) try: pages.extend(analyze_page_stat(perf_click(ui_worker_pid, prod_tail, True, accordion.tree(acc_tree).click_path, *path), soft_assert)) count += 1 # Navigate out of the page every 4th click if (count % 4) == 0: pages.extend(analyze_page_stat(perf_click(ui_worker_pid, prod_tail, False, sel.force_navigate, 'dashboard'), soft_assert)) pages.extend(analyze_page_stat(perf_click(ui_worker_pid, prod_tail, False, sel.force_navigate, page_name), soft_assert)) except CandidateNotFound: logger.info('Could not navigate to: '.format(path[-1])) except UnexpectedAlertPresentException: logger.warning('UnexpectedAlertPresentException - page_name: {}, accordion: {},' ' path: {}'.format(page_name, acc_tree, path[-1])) browser().switch_to_alert().dismiss() if not nav_limit == 0 and count >= nav_limit: break return pages
def drag_and_drop_by_offset(source_element, x=0, y=0): """Drag and Drop element by offset Args: source_element: A locator, expects either a string, WebElement, tuple. x: Distance in pixels on X axis to move it. y: Distance in pixels on Y axis to move it. """ e = move_to_element(source_element) ActionChains(browser()).drag_and_drop_by_offset(e, x, y).perform()
def drag_and_drop(source_element, dest_element): """Drag and Drop element. Args: source_element: A locator, expects either a string, WebElement, tuple. dest_element: A locator, expects either a string, WebElement, tuple. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) """ ActionChains(browser()).drag_and_drop(dest_element, source_element).perform()
def handle_alert(cancel=False, wait=30.0, squash=False): """Handles an alert popup. Args: cancel: Whether or not to cancel the alert. Accepts the Alert (False) by default. wait: Time to wait for an alert to appear. Default 30 seconds, can be set to 0 to disable waiting. squash: Whether or not to squash errors during alert handling. Default False Returns: True if the alert was handled, False if exceptions were squashed, None if there was no alert. No exceptions will be raised if ``squash`` is True. Raises: utils.wait.TimedOutError: If the alert popup does not appear selenium.common.exceptions.NoAlertPresentException: If no alert is present when accepting or dismissing the alert. """ # throws timeout exception if not found try: if wait: WebDriverWait(browser(), wait).until(expected_conditions.alert_is_present()) popup = browser().switch_to_alert() answer = 'cancel' if cancel else 'ok' logger.info('Handling popup "%s", clicking %s' % (popup.text, answer)) popup.dismiss() if cancel else popup.accept() wait_for_ajax() return True except NoAlertPresentException: return None except Exception as e: logger.exception(e) if squash: return False else: raise
def handle_alert(cancel=False, wait=30.0, squash=False): """Handles an alert popup. Args: cancel: Whether or not to cancel the alert. Accepts the Alert (False) by default. wait: Time to wait for an alert to appear. Default 30 seconds, can be set to 0 to disable waiting. squash: Whether or not to squash errors during alert handling. Default False Returns: True if the alert was handled, False if exceptions were squashed, None if there was no alert. No exceptions will be raised if ``squash`` is True. Raises: utils.wait.TimedOutError: If the alert popup does not appear selenium.common.exceptions.NoAlertPresentException: If no alert is present when accepting or dismissing the alert. """ # throws timeout exception if not found try: if wait: WebDriverWait(browser(), wait).until(expected_conditions.alert_is_present()) popup = browser().switch_to_alert() answer = 'cancel' if cancel else 'ok' logger.info('Handling popup "%s", clicking %s' % (popup.text, answer)) popup.dismiss() if cancel else popup.accept() wait_for_ajax() return True except NoAlertPresentException: return None except: if squash: return False else: raise
def send_keys(loc, text): """ Sends the supplied keys to an element. Args: loc: A locator, expects either a string, WebElement, tuple. text: The text to inject into the element. """ if text is not None: el = element(loc) ActionChains(browser()).move_to_element(el).send_keys_to_element(el, text).perform() wait_for_ajax()
def click(loc, wait_ajax=True): """ Clicks on an element. Args: loc: A locator, expects either a string, WebElement, tuple. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) """ ActionChains(browser()).move_to_element(element(loc)).click().perform() if wait_ajax: wait_for_ajax()
def set_text(loc, text): """ Clears the element and then sends the supplied keys. Args: loc: A locator, expects either a string, WebElement, tuple. text: The text to inject into the element. """ if text is not None: el = element(loc) ActionChains(browser()).move_to_element(el).perform() el.clear() el.send_keys(text)
def force_navigate(page_name, _tries=0, *args, **kwargs): """force_navigate(page_name) Given a page name, attempt to navigate to that page no matter what breaks. Args: page_name: Name a page from the current :py:data:`ui_navigate.nav_tree` tree to navigate to. """ if _tries >= 3: # Need at least three tries: # 1: login_admin handles an alert or closes the browser due any error # 2: If login_admin handles an alert, go_to can still encounter an unexpected error # 3: Everything should work. If not, NavigationError. raise exceptions.NavigationError(page_name) _tries += 1 logger.debug('force_navigate to %s, try %d' % (page_name, _tries)) # circular import prevention: cfme.login uses functions in this module from cfme import login # Import the top-level nav menus for convenience from cfme.web_ui import menu # NOQA # browser fixture should do this, but it's needed for subsequent calls ensure_browser_open() # Clear any running "spinnies" try: browser().execute_script('miqSparkleOff();') except: # miqSparkleOff undefined, so it's definitely off. pass try: # What we'd like to happen... login.login_admin() logger.info('Navigating to %s' % page_name) ui_navigate.go_to(page_name, *args, **kwargs) except (KeyboardInterrupt, ValueError): # KeyboardInterrupt: Don't block this while navigating # ValueError: ui_navigate.go_to can't handle this page, give up raise except UnexpectedAlertPresentException: if _tries == 1: # There was an alert, accept it and try again handle_alert(wait=0) else: # There was still an alert when we tried again, shoot the browser in the head logger.debug("Unxpected alert on try %d, recycling browser" % _tries) browser().quit() force_navigate(page_name, _tries, *args, **kwargs) except Exception as ex: # Anything else happened, nuke the browser and try again. logger.info('Caught %s during navigation, trying again.' % type(ex).__name__) logger.debug(format_exc()) browser().quit() force_navigate(page_name, _tries, *args, **kwargs)
def double_click(loc, wait_ajax=True): """Double-clicks on an element. Args: loc: A locator, expects either a string, WebElement, tuple. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) """ # Move mouse cursor to element move_to_element(loc) # and then click on current mouse position ActionChains(browser()).double_click().perform() # -> using this approach, we don't check if we clicked a specific element if wait_ajax: wait_for_ajax()
def double_click(loc, wait_ajax=True): """Double-clicks on an element. Args: loc: A locator, expects either a string, WebElement, tuple. wait_ajax: Whether to wait for ajax call to finish. Default True but sometimes it's handy to not do that. (some toolbar clicks) """ # Move mouse cursor to element move_to_element(loc) # and then click on current mouse position ActionChains(browser()).double_click().perform() # -> using this approach, we don't check if we clicked a specific element if wait_ajax: wait_for_ajax() return True
def take_screenshot(): screenshot = None screenshot_error = None try: screenshot = browser().get_screenshot_as_base64() except (AttributeError, WebDriverException): # See comments utils.browser.ensure_browser_open for why these two exceptions screenshot_error = 'browser error' except Exception as ex: # If this fails for any other reason, # leave out the screenshot but record the reason if str(ex): screenshot_error = '{}: {}'.format(type(ex).__name__, str(ex)) else: screenshot_error = type(ex).__name__ return ScreenShot(screenshot, screenshot_error)
def take_screenshot(): screenshot = None screenshot_error = None try: screenshot = browser().get_screenshot_as_base64() except (AttributeError, WebDriverException): # See comments utils.browser.ensure_browser_open for why these two exceptions screenshot_error = 'browser error' except Exception as ex: # If this fails for any other reason, # leave out the screenshot but record the reason if ex.message: screenshot_error = '%s: %s' % (type(ex).__name__, ex.message) else: screenshot_error = type(ex).__name__ return ScreenShot(screenshot, screenshot_error)
def checkbox(loc, set_to=False): """ Checks or unchecks a given checkbox Finds an element given by loc and checks it Args: loc: The locator of the element value: The value the checkbox should represent as a bool (or None to do nothing) Returns: None """ if set_to is not None: el = element(loc) ActionChains(browser()).move_to_element(el).perform() if el.is_selected() is not set_to: click(el)
def _t(t, root=None): """Assume tuple is a 2-item tuple like (By.ID, 'myid'). Handles the case when root= locator resolves to multiple elements. In that case all of them are processed and all results are put in the same list.""" result = [] for root_element in (elements(root) if root is not None else [browser()]): # 20140920 - dajo - hack to get around selenium e is null bs count = 0 while count < 8: count += 1 try: result += root_element.find_elements(*t) break except Exception as e: logger.info('Exception detected: ' + str(e)) sleep(0.25) if count == 8: result += root_element.find_elements(*t) return result
def _t(t, root=None): """Assume tuple is a 2-item tuple like (By.ID, 'myid'). Handles the case when root= locator resolves to multiple elements. In that case all of them are processed and all results are put in the same list.""" result = [] for root_element in (elements(root) if root is not None else [browser()]): # 20140920 - dajo - hack to get around selenium e is null bs count = 0 while count < 8: count += 1 try: result += root_element.find_elements(*t) break except Exception as e: logger.info('Exception detected: ' + str(e)) sleep(0.25) if count == 8: result += root_element.find_elements(*t) # Monkey patch them for elem in result: elem._source_locator = (t, root) return result
def force_navigate(page_name, _tries=0, *args, **kwargs): """force_navigate(page_name) Given a page name, attempt to navigate to that page no matter what breaks. Args: page_name: Name a page from the current :py:data:`ui_navigate.nav_tree` tree to navigate to. """ if _tries > 2: # Need at least three tries: # 1: login_admin handles an alert or CannotContinueWithNavigation appears. # 2: Everything should work. If not, NavigationError. raise exceptions.NavigationError(page_name) if "context" in kwargs: if not isinstance(kwargs["context"], ContextWrapper) and isinstance( kwargs["context"], dict): kwargs["context"] = ContextWrapper(kwargs["context"]) _tries += 1 logger.debug('force_navigate to %s, try %d' % (page_name, _tries)) # circular import prevention: cfme.login uses functions in this module from cfme import login # Import the top-level nav menus for convenience from cfme.web_ui import menu # browser fixture should do this, but it's needed for subsequent calls ensure_browser_open() # Clear any running "spinnies" try: execute_script('miqSparkleOff();') except: # Diaper OK (mfalesni) # miqSparkleOff undefined, so it's definitely off. pass # Set this to True in the handlers below to trigger a browser restart recycle = False # remember the current user, if any current_user = login.current_user() # Check if the page is blocked with blocker_div. If yes, let's headshot the browser right here if ( is_displayed("//div[@id='blocker_div' or @id='notification']", _no_deeper=True) or is_displayed(".modal-backdrop.fade.in", _no_deeper=True)): logger.warning("Page was blocked with blocker div on start of navigation, recycling.") quit() kwargs.pop("start", None) force_navigate("dashboard") # Start fresh # Check if modal window is displayed if (is_displayed( "//div[contains(@class, 'modal-dialog') and contains(@class, 'modal-lg')]", _no_deeper=True)): logger.warning("Modal window was open; closing the window") click("//button[contains(@class, 'close') and contains(@data-dismiss, 'modal')]") # Check if jQuery present try: execute_script("jQuery") except Exception as e: if "jQuery" not in str(e): logger.error("Checked for jQuery but got something different.") logger.exception(e) # Restart some workers logger.warning("Restarting UI and VimBroker workers!") with store.current_appliance.ssh_client as ssh: # Blow off the Vim brokers and UI workers ssh.run_rails_command("\"(MiqVimBrokerWorker.all + MiqUiWorker.all).each &:kill\"") logger.info("Waiting for web UI to come back alive.") sleep(10) # Give it some rest store.current_appliance.wait_for_web_ui() quit() ensure_browser_open() kwargs.pop("start", None) force_navigate("dashboard") # And start fresh # Same with rails errors rails_e = get_rails_error() if rails_e is not None: logger.warning("Page was blocked by rails error, renavigating.") logger.error(rails_e) logger.debug('Top CPU consumers:') logger.debug(store.current_appliance.ssh_client.run_command( 'top -c -b -n1 -M | head -30').output) logger.debug('Top Memory consumers:') logger.debug(store.current_appliance.ssh_client.run_command( 'top -c -b -n1 -M -a | head -30').output) logger.debug('Managed Providers:') logger.debug(store.current_appliance.managed_providers) quit() # Refresh the session, forget loaded summaries, ... kwargs.pop("start", None) ensure_browser_open() menu.nav.go_to("dashboard") # If there is a rails error past this point, something is really awful def _login_func(): if not current_user: # default to admin user login.login_admin() else: # we recycled and want to log back in login.login(current_user.username, current_user.password) try: try: # What we'd like to happen... _login_func() except WebDriverException as e: if "jquery" not in str(e).lower(): raise # Something unknown happened logger.info("Seems we got a non-CFME page (blank or screwed up) so killing the browser") quit() ensure_browser_open() # And try it again _login_func() # If this failed, no help with that :/ logger.info('Navigating to %s' % page_name) menu.nav.go_to(page_name, *args, **kwargs) except (KeyboardInterrupt, ValueError): # KeyboardInterrupt: Don't block this while navigating # ValueError: ui_navigate.go_to can't handle this page, give up raise except UnexpectedAlertPresentException: if _tries == 1: # There was an alert, accept it and try again handle_alert(wait=0) force_navigate(page_name, _tries, *args, **kwargs) else: # There was still an alert when we tried again, shoot the browser in the head logger.debug('Unxpected alert, recycling browser') recycle = True except (ErrorInResponseException, InvalidSwitchToTargetException): # Unable to switch to the browser at all, need to recycle logger.info('Invalid browser state, recycling browser') recycle = True except exceptions.CFMEExceptionOccured as e: # We hit a Rails exception logger.info('CFME Exception occured') logger.exception(e) recycle = True except exceptions.CannotContinueWithNavigation as e: # The some of the navigation steps cannot succeed logger.info('Cannot continue with navigation due to: %s; Recycling browser' % str(e)) recycle = True except (NoSuchElementException, InvalidElementStateException, WebDriverException) as e: from cfme.web_ui import cfme_exception as cfme_exc # To prevent circular imports # First check - if jquery is not found, there can be also another reason why this happened # so do not put the next branches in elif if isinstance(e, WebDriverException) and "jQuery" in str(e): # UI failed in some way, try recycling the browser logger.exception( "UI failed in some way, jQuery not found, (probably) recycling the browser.") recycle = True # If the page is blocked, then recycle... if ( is_displayed("//div[@id='blocker_div' or @id='notification']", _no_deeper=True) or is_displayed(".modal-backdrop.fade.in", _no_deeper=True)): logger.warning("Page was blocked with blocker div, recycling.") recycle = True elif cfme_exc.is_cfme_exception(): logger.exception("CFME Exception before force_navigate started!: `{}`".format( cfme_exc.cfme_exception_text() )) recycle = True elif is_displayed("//body[./h1 and ./p and ./hr and ./address]", _no_deeper=True): # 503 and similar sort of errors title = text("//body/h1") body = text("//body/p") logger.exception("Application error '{}': {}".format(title, body)) sleep(5) # Give it a little bit of rest recycle = True elif is_displayed("//body/div[@class='dialog' and ./h1 and ./p]", _no_deeper=True): # Rails exception detection logger.exception("Rails exception before force_navigate started!: {}:{} at {}".format( text("//body/div[@class='dialog']/h1").encode("utf-8"), text("//body/div[@class='dialog']/p").encode("utf-8"), current_url() )) recycle = True elif elements("//ul[@id='maintab']/li[@class='inactive']") and not\ elements("//ul[@id='maintab']/li[@class='active']/ul/li"): # If upstream and is the bottom part of menu is not displayed logger.exception("Detected glitch from BZ#1112574. HEADSHOT!") recycle = True else: logger.error("Could not determine the reason for failing the navigation. " + " Reraising. Exception: %s" % str(e)) logger.debug(store.current_appliance.ssh_client.run_command( 'service evmserverd status').output) raise if recycle: browser().quit() # login.current_user() will be retained for next login logger.debug('browser killed on try %d' % _tries) # If given a "start" nav destination, it won't be valid after quitting the browser kwargs.pop("start", None) force_navigate(page_name, _tries, *args, **kwargs)
def auth_finalizer(): browser.browser().refresh() login.login_admin()
def execute_script(script, *args, **kwargs): """Wrapper for execute_script() to not have to pull browser() from somewhere. It also provides our library which is stored in data/lib.js file. """ return browser().execute_script(dedent(script), *args, **kwargs)
def force_navigate(page_name, _tries=0, *args, **kwargs): """force_navigate(page_name) Given a page name, attempt to navigate to that page no matter what breaks. Args: page_name: Name a page from the current :py:data:`ui_navigate.nav_tree` tree to navigate to. """ if _tries > 2: # Need at least three tries: # 1: login_admin handles an alert or CannotContinueWithNavigation appears. # 2: Everything should work. If not, NavigationError. raise exceptions.NavigationError(page_name) _tries += 1 logger.debug('force_navigate to %s, try %d' % (page_name, _tries)) # circular import prevention: cfme.login uses functions in this module from cfme import login # Import the top-level nav menus for convenience from cfme.web_ui import menu # browser fixture should do this, but it's needed for subsequent calls ensure_browser_open() # Clear any running "spinnies" try: execute_script('miqSparkleOff();') except: # miqSparkleOff undefined, so it's definitely off. pass # Set this to True in the handlers below to trigger a browser restart recycle = False # remember the current user, if any current_user = login.current_user() try: # What we'd like to happen... if not current_user: # default to admin user login.login_admin() else: # we recycled and want to log back in login.login(current_user.username, current_user.password) logger.info('Navigating to %s' % page_name) menu.nav.go_to(page_name, *args, **kwargs) except (KeyboardInterrupt, ValueError): # KeyboardInterrupt: Don't block this while navigating # ValueError: ui_navigate.go_to can't handle this page, give up raise except UnexpectedAlertPresentException: if _tries == 1: # There was an alert, accept it and try again handle_alert(wait=0) force_navigate(page_name, _tries, *args, **kwargs) else: # There was still an alert when we tried again, shoot the browser in the head logger.debug('Unxpected alert, recycling browser') recycle = True except (ErrorInResponseException, InvalidSwitchToTargetException): # Unable to switch to the browser at all, need to recycle logger.info('Invalid browser state, recycling browser') recycle = True except exceptions.CannotContinueWithNavigation as e: # The some of the navigation steps cannot succeed logger.info( 'Cannot continue with navigation due to: %s; Recycling browser' % str(e)) recycle = True except (NoSuchElementException, InvalidElementStateException, WebDriverException): from cfme.web_ui import cfme_exception as cfme_exc # To prevent circular imports # If the page is blocked, then recycle... if is_displayed("//div[@id='blocker_div']"): logger.warning("Page was blocked with blocker div, recycling.") recycle = True elif cfme_exc.is_cfme_exception(): logger.exception( "CFME Exception before force_navigate started!: `{}`".format( cfme_exc.cfme_exception_text())) recycle = True elif is_displayed("//body/div[@class='dialog' and ./h1 and ./p]"): # Rails exception detection logger.exception( "Rails exception before force_navigate started!: {}:{} at {}". format( text("//body/div[@class='dialog']/h1").encode("utf-8"), text("//body/div[@class='dialog']/p").encode("utf-8"), current_url())) recycle = True elif elements("//ul[@id='maintab']/li[@class='inactive']") and not\ elements("//ul[@id='maintab']/li[@class='active']/ul/li"): # If upstream and is the bottom part of menu is not displayed logger.exception("Detected glitch from BZ#1112574. HEADSHOT!") recycle = True else: logger.error( "Could not determine the reason for failing the navigation. Reraising." ) raise if recycle: browser().quit( ) # login.current_user() will be retained for next login logger.debug('browser killed on try %d' % _tries) # If given a "start" nav destination, it won't be valid after quitting the browser kwargs.pop("start", None) force_navigate(page_name, _tries, *args, **kwargs)
def refresh(): """ Refreshes the current browser window. """ browser().refresh()
def title(): return browser().title
def wait_until(f, msg="Webdriver wait timed out", timeout=120.0): """This used to be a wrapper around WebDriverWait from selenium. Now it is just compatibility layer using :py:func:`utils.wait.wait_for` """ return wait_for(lambda: f(browser()), num_sec=timeout, message=msg, delay=0.5)
def force_navigate(page_name, _tries=0, *args, **kwargs): """force_navigate(page_name) Given a page name, attempt to navigate to that page no matter what breaks. Args: page_name: Name a page from the current :py:data:`ui_navigate.nav_tree` tree to navigate to. """ if _tries > 2: # Need at least three tries: # 1: login_admin handles an alert or CannotContinueWithNavigation appears. # 2: Everything should work. If not, NavigationError. raise exceptions.NavigationError(page_name) _tries += 1 logger.debug('force_navigate to %s, try %d' % (page_name, _tries)) # circular import prevention: cfme.login uses functions in this module from cfme import login # Import the top-level nav menus for convenience from cfme.web_ui import menu # browser fixture should do this, but it's needed for subsequent calls ensure_browser_open() # Clear any running "spinnies" try: execute_script('miqSparkleOff();') except: # miqSparkleOff undefined, so it's definitely off. pass # Set this to True in the handlers below to trigger a browser restart recycle = False # remember the current user, if any current_user = login.current_user() try: # What we'd like to happen... if not current_user: # default to admin user login.login_admin() else: # we recycled and want to log back in login.login(current_user.username, current_user.password) logger.info('Navigating to %s' % page_name) menu.nav.go_to(page_name, *args, **kwargs) except (KeyboardInterrupt, ValueError): # KeyboardInterrupt: Don't block this while navigating # ValueError: ui_navigate.go_to can't handle this page, give up raise except UnexpectedAlertPresentException: if _tries == 1: # There was an alert, accept it and try again handle_alert(wait=0) force_navigate(page_name, _tries, *args, **kwargs) else: # There was still an alert when we tried again, shoot the browser in the head logger.debug('Unxpected alert, recycling browser') recycle = True except (ErrorInResponseException, InvalidSwitchToTargetException): # Unable to switch to the browser at all, need to recycle logger.info('Invalid browser state, recycling browser') recycle = True except exceptions.CannotContinueWithNavigation as e: # The some of the navigation steps cannot succeed logger.info('Cannot continue with navigation due to: %s; Recycling browser' % str(e)) recycle = True except (NoSuchElementException, InvalidElementStateException, WebDriverException): from cfme.web_ui import cfme_exception as cfme_exc # To prevent circular imports # If the page is blocked, then recycle... if is_displayed("//div[@id='blocker_div']"): logger.warning("Page was blocked with blocker div, recycling.") recycle = True elif cfme_exc.is_cfme_exception(): logger.exception("CFME Exception before force_navigate started!: `{}`".format( cfme_exc.cfme_exception_text() )) recycle = True elif is_displayed("//body/div[@class='dialog' and ./h1 and ./p]"): # Rails exception detection logger.exception("Rails exception before force_navigate started!: {}:{} at {}".format( text("//body/div[@class='dialog']/h1").encode("utf-8"), text("//body/div[@class='dialog']/p").encode("utf-8"), current_url() )) recycle = True elif elements("//ul[@id='maintab']/li[@class='inactive']") and not\ elements("//ul[@id='maintab']/li[@class='active']/ul/li"): # If upstream and is the bottom part of menu is not displayed logger.exception("Detected glitch from BZ#1112574. HEADSHOT!") recycle = True else: logger.error("Could not determine the reason for failing the navigation. Reraising.") raise if recycle: browser().quit() # login.current_user() will be retained for next login logger.debug('browser killed on try %d' % _tries) # If given a "start" nav destination, it won't be valid after quitting the browser kwargs.pop("start", None) force_navigate(page_name, _tries, *args, **kwargs)
def wait_for_ajax(): """ Waits until all ajax timers are complete, in other words, waits until there are no more pending ajax requests, page load should be finished completely. Raises: TimedOutError: when ajax did not load in time """ _thread_local.ajax_log_msg = '' def _nothing_in_flight(): """Checks if there is no ajax in flight and also logs current status """ prev_log_msg = _thread_local.ajax_log_msg try: running = in_flight() except Exception as e: # if jQuery in error message, a non-cfme page (proxy error) is displayed # should be handled by something else if "jquery" not in str(e).lower(): raise return True anything_in_flight = False anything_in_flight |= running["jquery"] > 0 anything_in_flight |= running["prototype"] > 0 anything_in_flight |= running["spinner"] anything_in_flight |= running["document"] != "complete" log_msg = ', '.join(["{}: {}".format(k, str(v)) for k, v in running.iteritems()]) # Log the message only if it's different from the last one if prev_log_msg != log_msg: _thread_local.ajax_log_msg = log_msg logger.trace('Ajax running: {}'.format(log_msg)) if (not anything_in_flight) and prev_log_msg: logger.trace('Ajax done') return not anything_in_flight wait_for( _nothing_in_flight, num_sec=_thread_local.ajax_timeout, delay=0.1, message="wait for ajax", quiet=True, silent_failure=True) # If we are not supposed to take page screenshots...well...then...dont. if store.config and not store.config.getvalue('page_screenshots'): return url = browser().current_url url = url.replace(base_url(), '') url = url.replace("/", '_') if url not in urls: logger.info('Taking picture of page: {}'.format(url)) ss, sse = take_screenshot() if ss: ss_path = log_path.join('page_screenshots') if not ss_path.exists(): ss_path.mkdir() with ss_path.join("{}.png".format(url)).open('wb') as f: f.write(base64.b64decode(ss)) urls.append(url)
def selenium(self): return browser()
def wait_until(f, msg="Webdriver wait timed out"): """ Wrapper around WebDriverWait from selenium """ t = time() return WebDriverWait(browser(), 120.0).until(f, msg) and time() - t