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, provider, 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` """ if provider.one_of(RHEVMProvider) and provisioning.get('vlan') is None: pytest.skip('rhevm requires a vlan value in provisioning info') 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 = appliance.collections.infra_vms.instantiate(name=vm_name, provider=provider, template_name=template_name) view = navigate_to(vm.parent, 'Provision') provisioning_data = { 'request': { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes}, 'catalog': { 'vm_name': vm_name, 'num_vms': '10'}, 'environment': {'host_name': {'name': host}, 'datastore_name': {'name': datastore}}, 'network': {'vlan': partial_match(provisioning['vlan'] if provisioning.get('vlan') else None)} } # Same thing, different names. :\ if provider.one_of(RHEVMProvider): provisioning_data['catalog']['provision_type'] = 'Native Clone' elif provider.one_of(VMwareProvider): provisioning_data['catalog']['provision_type'] = 'VMware' # template and provider names for template selection provisioning_data['template_name'] = template_name provisioning_data['provider_name'] = provider.name view.form.fill_with(provisioning_data, on_change=view.form.submit_button) request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } provision_request = appliance.collections.requests.instantiate(cells=request_cells) yield provision_request browser().get(appliance.url) appliance.server.login_admin() provision_request.remove_request()
def generated_request(appliance, a_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=a_provider, template_name=template_name) view = navigate_to(vm, 'Provision') provisioning_data = { 'request': { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes}, 'catalog': { 'vm_name': vm_name, 'num_vms': '10'}, 'environment': {'host_name': {'name': host}, 'datastore_name': {'name': datastore}}, } # Same thing, different names. :\ if provider_data["type"] == 'rhevm': provisioning_data['catalog']['provision_type'] = 'Native Clone' elif provider_data["type"] == 'virtualcenter': provisioning_data['catalog']['provision_type'] = 'VMware' try: provisioning_data['network'] = {'vlan': partial_match(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') view.form.fill_with(provisioning_data, on_change=view.form.submit_button) request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } provision_request = appliance.collections.requests.instantiate(cells=request_cells) yield provision_request browser().get(store.base_url) appliance.server.login_admin() provision_request.remove_request()
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 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(wait_ajax=wait_ajax) # 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() return True
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 current_url(): """ Returns the current_url of the page Returns: A url. """ return browser().current_url
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 needs_firefox(): """ Fixture which skips the test if not run under firefox. I recommend putting it in the first place. """ ensure_browser_open() if browser.browser().name != "firefox": pytest.skip(msg="This test needs firefox to run")
def get(url): """ Changes page to the specified URL Args: url: URL to navigate to. """ return browser().get(url)
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(source_element, dest_element).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 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 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 pytest_runtest_teardown(item, nextitem): if item.config.getoption('sauce'): from cfme.utils.browser import ensure_browser_open, quit, browser ensure_browser_open() browser().execute_script(f"sauce:job-name={item.name}") quit()
def generated_request(appliance, a_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 = InfraVm(name=vm_name, provider=a_provider, template_name=template_name) view = navigate_to(vm, 'Provision') provisioning_data = { 'request': { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes }, 'catalog': { 'vm_name': vm_name, 'num_vms': '10' }, 'environment': { 'host_name': { 'name': host }, 'datastore_name': { 'name': datastore } }, } # Same thing, different names. :\ if provider_data["type"] == 'rhevm': provisioning_data['catalog']['provision_type'] = 'Native Clone' elif provider_data["type"] == 'virtualcenter': provisioning_data['catalog']['provision_type'] = 'VMware' try: provisioning_data['network'] = { 'vlan': partial_match(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') view.form.fill_with(provisioning_data, on_change=view.form.submit_button) request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } provision_request = appliance.collections.requests.instantiate( cells=request_cells) yield provision_request browser().get(appliance.url) appliance.server.login_admin() provision_request.remove_request()
def refresh(): """ Refreshes the current browser window. """ browser().refresh()
def generated_request(appliance, provider, 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` """ if provider.one_of(RHEVMProvider) and provisioning.get('vlan') is None: pytest.skip('rhevm requires a vlan value in provisioning info') first_name = fauxfactory.gen_alphanumeric() last_name = fauxfactory.gen_alphanumeric() notes = fauxfactory.gen_alphanumeric() e_mail = "{}@{}.test".format(first_name, last_name) host, datastore = list(map(provisioning.get, ('host', 'datastore'))) vm = appliance.collections.infra_vms.instantiate( name=vm_name, provider=provider, template_name=template_name) view = navigate_to(vm.parent, 'Provision') provisioning_data = { 'request': { 'email': e_mail, 'first_name': first_name, 'last_name': last_name, 'notes': notes }, 'catalog': { 'vm_name': vm_name, 'num_vms': '10' }, 'environment': { 'host_name': { 'name': host }, 'datastore_name': { 'name': datastore } }, 'network': { 'vlan': partial_match( provisioning['vlan'] if provisioning.get('vlan') else None) } } # Same thing, different names. :\ if provider.one_of(RHEVMProvider): provisioning_data['catalog']['provision_type'] = 'Native Clone' elif provider.one_of(VMwareProvider): provisioning_data['catalog']['provision_type'] = 'VMware' # template and provider names for template selection provisioning_data['template_name'] = template_name provisioning_data['provider_name'] = provider.name view.form.fill_with(provisioning_data, on_change=view.form.submit_button) request_cells = { "Description": "Provision from [{}] to [{}###]".format(template_name, vm_name), } provision_request = appliance.collections.requests.instantiate( cells=request_cells) yield provision_request browser().get(appliance.url) appliance.server.login_admin() provision_request.remove_request()
def pytest_runtest_teardown(item, nextitem): if item.config.getoption('sauce'): from cfme.utils.browser import ensure_browser_open, quit, browser ensure_browser_open() browser().execute_script("sauce:job-name={}".format(item.name)) quit()
def title(): return browser().title
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, 'Provision') 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), } provision_request = RequestCollection(appliance).instantiate( cells=request_cells) yield provision_request browser().get(store.base_url) appliance.server.login_admin() provision_request.remove_request() flash.assert_no_errors()
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 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 """ execute_script(""" try { angular.element('error-modal').hide(); } catch(err) { }""") _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 # 5.5.z and 5.7.0.4+ if not store.current_appliance.is_miqqe_patch_candidate: try: anything_in_flight = in_flight("return ManageIQ.qe.anythingInFlight()") 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 running = execute_script("return ManageIQ.qe.inFlight()") log_msg = ', '.join(["{}: {}".format(k, str(v)) for k, v in running.iteritems()]) # 5.6.z, 5.7.0.{1,2,3} else: try: running = in_flight(js.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" anything_in_flight |= running["autofocus"] > 0 anything_in_flight |= running["debounce"] > 0 anything_in_flight |= running["miqQE"] > 0 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: %s', 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: %s', 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 get_alert(): return browser().switch_to_alert()
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 """ execute_script(""" try { angular.element('error-modal').hide(); } catch(err) { }""") _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 # 5.5.z and 5.7.0.4+ if not store.current_appliance.is_miqqe_patch_candidate: try: anything_in_flight = in_flight( "return ManageIQ.qe.anythingInFlight()") 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 running = execute_script("return ManageIQ.qe.inFlight()") log_msg = ', '.join( ["{}: {}".format(k, str(v)) for k, v in running.iteritems()]) # 5.6.z, 5.7.0.{1,2,3} else: try: running = in_flight(js.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" anything_in_flight |= running["autofocus"] > 0 anything_in_flight |= running["debounce"] > 0 anything_in_flight |= running["miqQE"] > 0 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: %s', 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: %s', 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)