class DBReporting(Plugin): """ This plugin records test results in the Testcase Database. """ name = 'db_reporting' # Usage: --with-db_reporting def __init__(self): Plugin.__init__(self) self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False def options(self, parser, env): super(DBReporting, self).options(parser, env=env) parser.add_option('--database_environment', action='store', dest='database_env', choices=('production', 'qa', 'staging', 'develop', 'test', 'local', 'master'), default='test', help=SUPPRESS_HELP) def configure(self, options, conf): super(DBReporting, self).configure(options, conf) self.options = options self.testcase_manager = TestcaseManager(self.options.database_env) def begin(self): """ At the start of the run, we want to record the test execution information in the database. """ exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) def startTest(self, test): """ At the start of the test, set the testcase details. """ data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if hasattr(test, "browser"): data_payload.browser = test.browser else: data_payload.browser = "N/A" data_payload.test_address = test.id() application = ApplicationManager.generate_application_string(test) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) # Make the testcase guid available to other plugins test.testcase_guid = self.testcase_guid def finalize(self, result): """ At the end of the run, we want to update the DB row with the execution time. """ runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data(self.execution_guid, runtime) def addSuccess(self, test, capt): """ After test completion, we want to record testcase run information. """ self.__insert_test_result(constants.State.PASS, test) def addError(self, test, err, capt=None): """ After a test error, we want to record testcase run information. """ self.__insert_test_result(constants.State.ERROR, test, err) def handleError(self, test, err, capt=None): """ After a test error, we want to record testcase run information. "Error" also encompasses any states other than Pass or Fail, so we check for those first. """ if err[0] == errors.BlockedTest: self.__insert_test_result(constants.State.BLOCKED, test, err) self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.DeprecatedTest: self.__insert_test_result(constants.State.DEPRECATED, test, err) self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.SkipTest: self.__insert_test_result(constants.State.SKIP, test, err) self.error_handled = True raise SkipTest(err[1]) return True def addFailure(self, test, err, capt=None, tbinfo=None): """ After a test failure, we want to record testcase run information. """ self.__insert_test_result(constants.State.FAILURE, test, err) def __insert_test_result(self, state, test, err=None): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err is not None: data_payload.message = err[1].__str__().split( '''-------------------- >> ''' '''begin captured logging''' ''' << --------------------''', 1)[0] self.testcase_manager.update_testcase_data(data_payload)
class BaseCase(unittest.TestCase): ''' A base test case that wraps methods for enhanced usage. You can also add your own methods here. ''' def __init__(self, *args, **kwargs): super(BaseCase, self).__init__(*args, **kwargs) try: self.driver = WebDriver() except Exception: pass self.environment = None self.page_check_count = 0 self.page_check_failures = [] def open(self, url): self.driver.get(url) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def open_url(self, url): """ In case people are mixing up self.open() with open(), use this alternative. """ self.open(url) def click(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def click_chain(self, selectors_list, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, spacing=0): """ This method clicks on a list of elements in succession. 'spacing' is the amount of time to wait between clicks. (sec) """ for selector in selectors_list: self.click(selector, by=by, timeout=timeout) if spacing > 0: time.sleep(spacing) def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): """ This method clicks link text on a page """ # If using phantomjs, might need to extract and open the link directly if self.browser == 'phantomjs': if self.is_link_text_visible(link_text): element = self.wait_for_link_text_visible(link_text) element.click() return source = self.driver.page_source soup = BeautifulSoup(source) html_links = soup.fetch('a') for html_link in html_links: if html_link.text == link_text: for html_attribute in html_link.attrs: if html_attribute[0] == 'href': href = html_attribute[1] if href.startswith('//'): link = "http:" + href elif href.startswith('/'): url = self.driver.current_url domain_url = self.get_domain_url(url) link = domain_url + href else: link = href self.open(link) return raise Exception( 'Could not parse link from link_text [%s]' % link_text) raise Exception("Link text [%s] was not found!" % link_text) # Not using phantomjs element = self.wait_for_link_text_visible(link_text, timeout=timeout) self._demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT) pre_action_url = self.driver.current_url element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def get_text(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout) return element.text def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_present( self.driver, selector, by, timeout) attribute_value = element.get_attribute(attribute) if attribute_value is not None: return attribute_value else: raise Exception("Element [%s] has no attribute [%s]!" % ( selector, attribute)) def add_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ The more-reliable version of driver.send_keys() Similar to update_text(), but won't clear the text field first. """ element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url element.send_keys(new_value) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def send_keys(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Same as add_text() -> more reliable, but less name confusion. """ self.add_text(selector, new_value, by=by, timeout=timeout) def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, retry=False): """ This method updates an element's text value with a new value. @Params selector - the selector with the value to update new_value - the new value for setting the text field by - the type of selector to search by (Default: CSS) timeout - how long to wait for the selector to be visible retry - if True, use jquery if the selenium text update fails """ element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) element.clear() self._demo_mode_pause_if_active(tiny=True) pre_action_url = self.driver.current_url element.send_keys(new_value) if (retry and element.get_attribute('value') != new_value and ( not new_value.endswith('\n'))): logging.debug('update_text_value is falling back to jQuery!') selector = self.jq_format(selector) self.set_value(selector, new_value, by=by) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def update_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, retry=False): """ The shorter version of update_text_value(), which clears existing text and adds new text into the text field. We want to keep the old version for backward compatibility. """ self.update_text_value(selector, new_value, by=by, timeout=timeout, retry=retry) def is_element_present(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_present(self.driver, selector, by) def is_element_visible(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_visible(self.driver, selector, by) def is_link_text_visible(self, link_text): return page_actions.is_element_visible(self.driver, link_text, by=By.LINK_TEXT) def is_text_visible(self, text, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_text_visible(self.driver, text, selector, by) def find_visible_elements(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.find_visible_elements(self.driver, selector, by) def execute_script(self, script): return self.driver.execute_script(script) def set_window_size(self, width, height): return self.driver.set_window_size(width, height) self._demo_mode_pause_if_active() def maximize_window(self): return self.driver.maximize_window() self._demo_mode_pause_if_active() def activate_jquery(self): """ If "jQuery is not defined", use this method to activate it for use. This happens because jQuery is not always defined on web sites. """ try: # Let's first find out if jQuery is already defined. self.execute_script("jQuery('html')") # Since that command worked, jQuery is defined. Let's return. return except Exception: # jQuery is not currently defined. Let's proceed by defining it. pass self.execute_script( '''var script = document.createElement("script"); ''' '''script.src = "http://code.jquery.com/jquery-2.2.4.min.js"; ''' '''document.getElementsByTagName("head")[0]''' '''.appendChild(script);''') for x in xrange(30): # jQuery needs a small amount of time to activate. (At most 3s) try: self.execute_script("jQuery('html')") return except Exception: time.sleep(0.1) # Since jQuery still isn't activating, give up and raise an exception raise Exception("Exception: WebDriver could not activate jQuery!") def highlight(self, selector, by=By.CSS_SELECTOR, loops=4, scroll=True): """ This method uses fancy javascript to highlight an element. Used during demo_mode. @Params selector - the selector of the element to find by - the type of selector to search by (Default: CSS) loops - # of times to repeat the highlight animation (Default: 4) (4 loops is about 0.70 seconds. Your mileage may vary) scroll - the option to scroll to the element first (Default: True) """ element = self.find_element( selector, by=by, timeout=settings.SMALL_TIMEOUT) if scroll: self._slow_scroll_to_element(element) try: selector = self.convert_to_css_selector(selector, by=by) except Exception: # Don't highlight if can't convert to CSS_SELECTOR for jQuery return # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' o_bs = '' # original_box_shadow style = element.get_attribute('style') if style: if 'box-shadow: ' in style: box_start = style.find('box-shadow: ') box_end = style.find(';', box_start) + 1 original_box_shadow = style[box_start:box_end] o_bs = original_box_shadow script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 128, 128, 0.5)');""" % selector try: self.execute_script(script) except Exception: self.activate_jquery() self.execute_script(script) loops = int(loops) for n in xrange(loops): script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(255, 0, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(0, 0, 255, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(0, 255, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 128, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs) self.execute_script(script) time.sleep(0.065) def scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): ''' Fast scroll to destination ''' element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._scroll_to_element(element) def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): ''' Slow motion scroll to destination ''' element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._slow_scroll_to_element(element) def scroll_click(self, selector, by=By.CSS_SELECTOR): self.scroll_to(selector, by=by) self.click(selector, by=by) def jquery_click(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH selector = self.convert_to_css_selector(selector, by=by) self.wait_for_element_present( selector, by=by, timeout=settings.SMALL_TIMEOUT) if self.is_element_visible(selector, by=by): self._demo_mode_highlight_if_active(selector, by) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' click_script = """jQuery('%s')[0].click()""" % selector try: self.execute_script(click_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(click_script) self._demo_mode_pause_if_active() def jq_format(self, code): return page_utils.jq_format(code) def get_domain_url(self, url): return page_utils.get_domain_url(url) def download_file(self, file_url, destination_folder=None): """ Downloads the file from the url to the destination folder. If no destination folder is specified, the default one is used. """ if not destination_folder: destination_folder = constants.Files.DOWNLOADS_FOLDER page_utils._download_file_to(file_url, destination_folder) return True def save_file_as(self, file_url, new_file_name, destination_folder=None): """ Similar to self.download_file(), except that you get to rename the file being downloaded to whatever you want. """ if not destination_folder: destination_folder = constants.Files.DOWNLOADS_FOLDER page_utils._download_file_to( file_url, destination_folder, new_file_name) return True def convert_xpath_to_css(self, xpath): return xpath_to_css.convert_xpath_to_css(xpath) def convert_to_css_selector(self, selector, by): """ This method converts a selector to a CSS_SELECTOR. jQuery commands require a CSS_SELECTOR for finding elements. This method should only be used for jQuery actions. """ if by == By.CSS_SELECTOR: return selector elif by == By.ID: return '#%s' % selector elif by == By.CLASS_NAME: return '.%s' % selector elif by == By.NAME: return '[name="%s"]' % selector elif by == By.TAG_NAME: return selector elif by == By.XPATH: return self.convert_xpath_to_css(selector) elif by == By.LINK_TEXT: return 'a:contains("%s")' % selector elif by == By.PARTIAL_LINK_TEXT: return 'a:contains("%s")' % selector else: raise Exception( "Exception: Could not convert [%s](by=%s) to CSS_SELECTOR!" % ( selector, by)) def set_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ This method uses jQuery to update a text field. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH selector = self.convert_to_css_selector(selector, by=by) self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by, timeout=timeout) value = json.dumps(new_value) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' set_value_script = """jQuery('%s').val(%s)""" % (selector, value) try: self.execute_script(set_value_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(set_value_script) self._demo_mode_pause_if_active() def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ This method uses jQuery to update a text field. If the new_value string ends with the newline character, WebDriver will finish the call, which simulates pressing {Enter/Return} after the text is entered. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = self.wait_for_element_visible( selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by) selector = self.convert_to_css_selector(selector, by=by) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' update_text_script = """jQuery('%s').val('%s')""" % ( selector, self.jq_format(new_value)) try: self.execute_script(update_text_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(update_text_script) if new_value.endswith('\n'): element.send_keys('\n') self._demo_mode_pause_if_active() def jquery_update_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ The shorter version of jquery_update_text_value() (The longer version remains for backwards compatibility.) """ self.jquery_update_text_value( selector, new_value, by=by, timeout=timeout) def hover_on_element(self, selector, by=By.CSS_SELECTOR): self.wait_for_element_visible( selector, by=by, timeout=settings.SMALL_TIMEOUT) self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by) time.sleep(0.05) # Settle down from scrolling before hovering return page_actions.hover_on_element(self.driver, selector) def hover_and_click(self, hover_selector, click_selector, hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if hover_selector.startswith('/') or hover_selector.startswith('./'): hover_by = By.XPATH if click_selector.startswith('/') or click_selector.startswith('./'): click_by = By.XPATH self.wait_for_element_visible( hover_selector, by=hover_by, timeout=timeout) self._demo_mode_highlight_if_active(hover_selector, hover_by) self.scroll_to(hover_selector, by=hover_by) pre_action_url = self.driver.current_url element = page_actions.hover_and_click( self.driver, hover_selector, click_selector, hover_by, click_by, timeout) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) return element ############ def wait_for_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to appear in the HTML of a page. The element does not need be visible (it may be hidden). """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_present( self.driver, selector, by, timeout) def assert_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_present(), but returns nothing. Waits for an element to appear in the HTML of a page. The element does not need be visible (it may be hidden). Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_present(selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to appear in the HTML of a page. The element must be visible (it cannot be hidden). """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_visible( self.driver, selector, by, timeout) def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_element_visible() """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def find_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_element_visible() - returns the element """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_visible(selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_text_visible(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_text_visible( self.driver, text, selector, by, timeout) def wait_for_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_text_visible() """ return self.wait_for_text_visible( text, selector, by=by, timeout=timeout) def find_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_text_visible() - returns the element """ return self.wait_for_text_visible( text, selector, by=by, timeout=timeout) def assert_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_text_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_text_visible(text, selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_link_text_visible(self, link_text, timeout=settings.LARGE_TIMEOUT): return self.wait_for_element_visible( link_text, by=By.LINK_TEXT, timeout=timeout) def wait_for_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_link_text_visible() """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def find_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_link_text_visible() - returns the element """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def assert_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_link_text_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_link_text_visible(link_text, timeout=timeout) return True ############ def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to no longer appear in the HTML of a page. A hidden element still counts as appearing in the page HTML. If an element with "hidden" status is acceptable, use wait_for_element_not_visible() instead. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_absent( self.driver, selector, by, timeout) def assert_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_absent() - returns nothing. As above, will raise an exception if the element stays present. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_absent(selector, by=by, timeout=timeout) return True ############ def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to no longer be visible on a page. The element can be non-existant in the HTML or hidden on the page to qualify as not visible. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_not_visible( self.driver, selector, by, timeout) def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_not_visible() - returns nothing. As above, will raise an exception if the element stays visible. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_not_visible(selector, by=by, timeout=timeout) return True ############ def wait_for_ready_state_complete(self, timeout=settings.EXTREME_TIMEOUT): return page_actions.wait_for_ready_state_complete(self.driver, timeout) def wait_for_and_accept_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_accept_alert(self.driver, timeout) def wait_for_and_dismiss_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_dismiss_alert(self.driver, timeout) def wait_for_and_switch_to_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_switch_to_alert(self.driver, timeout) def save_screenshot(self, name, folder=None): return page_actions.save_screenshot(self.driver, name, folder) ############ def _get_exception_message(self): """ This method extracts the message from an exception if there was an exception that occurred during the test, assuming that the exception was in a try/except block and not thrown. """ exception_info = sys.exc_info()[1] if hasattr(exception_info, 'msg'): exc_message = exception_info.msg elif hasattr(exception_info, 'message'): exc_message = exception_info.message else: exc_message = '(Unknown Exception)' return exc_message def _package_check(self): current_url = self.driver.current_url message = self._get_exception_message() self.page_check_failures.append( "CHECK #%s: (%s)\n %s" % ( self.page_check_count, current_url, message)) def check_assert_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.MINI_TIMEOUT): """ A non-terminating assertion for an element on a page. Any and all exceptions will be saved until the process_checks() method is called from inside a test, likely at the end of it. """ self.page_check_count += 1 try: self.wait_for_element_visible(selector, by=by, timeout=timeout) return True except Exception: self._package_check() return False def check_assert_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.MINI_TIMEOUT): """ A non-terminating assertion for text from an element on a page. Any and all exceptions will be saved until the process_checks() method is called from inside a test, likely at the end of it. """ self.page_check_count += 1 try: self.wait_for_text_visible(text, selector, by=by, timeout=timeout) return True except Exception: self._package_check() return False def process_checks(self): """ To be used at the end of any test that uses checks, which are non-terminating verifications that will only raise an exception after this method is called. Useful for pages with multiple elements to be checked when you want to find as many failures as possible on a page before making fixes. Might be more useful if this method is called after processing all the checks for a single html page, otherwise the screenshot in the logs file won't match the location of the checks. """ if self.page_check_failures: exception_output = '' exception_output += "\n*** FAILED CHECKS FOR: %s\n" % self.id() all_failing_checks = self.page_check_failures self.page_check_failures = [] for tb in all_failing_checks: exception_output += "%s\n" % tb raise Exception(exception_output) ############ def _demo_mode_pause_if_active(self, tiny=False): if self.demo_mode: if self.demo_sleep: wait_time = float(self.demo_sleep) else: wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT if not tiny: time.sleep(wait_time) else: time.sleep(wait_time/3.4) def _demo_mode_scroll_if_active(self, selector, by): if self.demo_mode: self.slow_scroll_to(selector, by=by) def _demo_mode_highlight_if_active(self, selector, by): if self.demo_mode: # Includes self.slow_scroll_to(selector, by=by) by default self.highlight(selector, by=by) def _scroll_to_element(self, element): element_location = element.location['y'] element_location = element_location - 130 if element_location < 0: element_location = 0 scroll_script = "window.scrollTo(0, %s);" % element_location # The old jQuery scroll_script required by=By.CSS_SELECTOR # scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector self.execute_script(scroll_script) self._demo_mode_pause_if_active(tiny=True) def _slow_scroll_to_element(self, element): scroll_position = self.execute_script("return window.scrollY;") element_location = element.location['y'] element_location = element_location - 130 if element_location < 0: element_location = 0 distance = element_location - scroll_position if distance != 0: total_steps = int(abs(distance) / 50.0) + 2.0 step_value = float(distance) / total_steps new_position = scroll_position for y in xrange(int(total_steps)): time.sleep(0.0114) new_position += step_value scroll_script = "window.scrollTo(0, %s);" % new_position self.execute_script(scroll_script) time.sleep(0.01) scroll_script = "window.scrollTo(0, %s);" % element_location self.execute_script(scroll_script) time.sleep(0.01) if distance > 430 or distance < -300: # Add small recovery time for long-distance slow-scrolling time.sleep(0.162) # PyTest-Specific Code # def setUp(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass setUp() method: super(SubClassOfBaseCase, self).setUp() """ self.is_pytest = None try: # This raises an exception if the test is not coming from pytest self.is_pytest = pytest.config.option.is_pytest except Exception: # Not using pytest (probably nosetests) self.is_pytest = False if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) self.with_selenium = pytest.config.option.with_selenium self.headless = pytest.config.option.headless self.headless_active = False self.with_testing_base = pytest.config.option.with_testing_base self.with_db_reporting = pytest.config.option.with_db_reporting self.with_s3_logging = pytest.config.option.with_s3_logging self.with_screen_shots = pytest.config.option.with_screen_shots self.with_basic_test_info = ( pytest.config.option.with_basic_test_info) self.with_page_source = pytest.config.option.with_page_source self.database_env = pytest.config.option.database_env self.log_path = pytest.config.option.log_path self.browser = pytest.config.option.browser self.data = pytest.config.option.data self.demo_mode = pytest.config.option.demo_mode self.demo_sleep = pytest.config.option.demo_sleep if self.with_db_reporting: self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False self.testcase_manager = TestcaseManager(self.database_env) # exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) # data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if self.with_selenium: data_payload.browser = self.browser else: data_payload.browser = "N/A" data_payload.testcaseAddress = test_id application = ApplicationManager.generate_application_string( self._testMethodName) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) if self.headless: self.display = Display(visible=0, size=(1200, 800)) self.display.start() self.headless_active = True if self.with_selenium: self.driver = browser_launcher.get_driver(self.browser) def __insert_test_result(self, state, err): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err: tb_string = traceback.format_exc() if "Message: " in tb_string: data_payload.message = "Message: " + tb_string.split( "Message: ")[-1] elif "Exception: " in tb_string: data_payload.message = tb_string.split("Exception: ")[-1] elif "Error: " in tb_string: data_payload.message = tb_string.split("Error: ")[-1] else: data_payload.message = "Unknown Error: See Stacktrace" self.testcase_manager.update_testcase_data(data_payload) def tearDown(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass's tearDown(): super(SubClassOfBaseCase, self).tearDown() """ if self.page_check_failures: # self.process_checks() was not called after checks were made. # We will log those now here, but without raising an exception. exception_output = '' exception_output += "\n*** FAILED CHECKS FOR: %s\n" % self.id() for tb in self.page_check_failures: exception_output += "%s\n" % tb logging.exception(exception_output) if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) if self.with_selenium: # Save a screenshot if logging is on when an exception occurs if self.with_testing_base and (sys.exc_info()[1] is not None): test_logpath = self.log_path + "/" + test_id if not os.path.exists(test_logpath): os.makedirs(test_logpath) if ((not self.with_screen_shots) and (not self.with_basic_test_info) and (not self.with_page_source)): # Log everything if nothing specified (if testing_base) log_helper.log_screenshot(test_logpath, self.driver) log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) log_helper.log_page_source(test_logpath, self.driver) else: if self.with_screen_shots: log_helper.log_screenshot( test_logpath, self.driver) if self.with_basic_test_info: log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) if self.with_page_source: log_helper.log_page_source( test_logpath, self.driver) # Finally close the browser self.driver.quit() if self.headless: if self.headless_active: self.display.stop() if self.with_db_reporting: if sys.exc_info()[1] is not None: self.__insert_test_result(constants.State.ERROR, True) else: self.__insert_test_result(constants.State.PASS, False) runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data( self.execution_guid, runtime) if self.with_s3_logging and (sys.exc_info()[1] is not None): """ After each testcase, upload logs to the S3 bucket. """ s3_bucket = S3LoggingBucket() guid = str(uuid.uuid4().hex) path = "%s/%s" % (self.log_path, test_id) uploaded_files = [] for logfile in os.listdir(path): logfile_name = "%s/%s/%s" % (guid, test_id, logfile.split(path)[-1]) s3_bucket.upload_file(logfile_name, "%s/%s" % (path, logfile)) uploaded_files.append(logfile_name) s3_bucket.save_uploaded_file_names(uploaded_files) index_file = s3_bucket.upload_index_file(test_id, guid) print("\n\n*** Log files uploaded: ***\n%s\n" % index_file) logging.error( "\n\n*** Log files uploaded: ***\n%s\n" % index_file) if self.with_db_reporting: self.testcase_manager = TestcaseManager(self.database_env) data_payload = TestcaseDataPayload() data_payload.guid = self.testcase_guid data_payload.logURL = index_file self.testcase_manager.update_testcase_log_url(data_payload)
class BaseCase(unittest.TestCase): ''' A base test case that wraps methods for enhanced usage. You can also add your own methods here. ''' def __init__(self, *args, **kwargs): super(BaseCase, self).__init__(*args, **kwargs) try: self.driver = WebDriver() except Exception: pass self.environment = None self.page_check_count = 0 self.page_check_failures = [] def open(self, url): self.driver.get(url) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def open_url(self, url): """ In case people are mixing up self.open() with open(), use this alternative. """ self.open(url) def click(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = page_actions.wait_for_element_visible(self.driver, selector, by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def click_chain(self, selectors_list, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, spacing=0): """ This method clicks on a list of elements in succession. 'spacing' is the amount of time to wait between clicks. (sec) """ for selector in selectors_list: self.click(selector, by=by, timeout=timeout) if spacing > 0: time.sleep(spacing) def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): """ This method clicks link text on a page """ # If using phantomjs, might need to extract and open the link directly if self.browser == 'phantomjs': if self.is_link_text_visible(link_text): element = self.wait_for_link_text_visible(link_text) element.click() return source = self.driver.page_source soup = BeautifulSoup(source) html_links = soup.fetch('a') for html_link in html_links: if html_link.text == link_text: for html_attribute in html_link.attrs: if html_attribute[0] == 'href': href = html_attribute[1] if href.startswith('//'): link = "http:" + href elif href.startswith('/'): url = self.driver.current_url domain_url = self.get_domain_url(url) link = domain_url + href else: link = href self.open(link) return raise Exception( 'Could not parse link from link_text [%s]' % link_text) raise Exception("Link text [%s] was not found!" % link_text) # Not using phantomjs element = self.wait_for_link_text_visible(link_text, timeout=timeout) self._demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT) pre_action_url = self.driver.current_url element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def get_text(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout) return element.text def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_present( self.driver, selector, by, timeout) attribute_value = element.get_attribute(attribute) if attribute_value is not None: return attribute_value else: raise Exception("Element [%s] has no attribute [%s]!" % (selector, attribute)) def add_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ The more-reliable version of driver.send_keys() Similar to update_text(), but won't clear the text field first. """ element = self.wait_for_element_visible(selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) pre_action_url = self.driver.current_url element.send_keys(new_value) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def send_keys(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Same as add_text() -> more reliable, but less name confusion. """ self.add_text(selector, new_value, by=by, timeout=timeout) def update_text_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, retry=False): """ This method updates an element's text value with a new value. @Params selector - the selector with the value to update new_value - the new value for setting the text field by - the type of selector to search by (Default: CSS) timeout - how long to wait for the selector to be visible retry - if True, use jquery if the selenium text update fails """ element = self.wait_for_element_visible(selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) element.clear() self._demo_mode_pause_if_active(tiny=True) pre_action_url = self.driver.current_url element.send_keys(new_value) if (retry and element.get_attribute('value') != new_value and (not new_value.endswith('\n'))): logging.debug('update_text_value is falling back to jQuery!') selector = self.jq_format(selector) self.set_value(selector, new_value, by=by) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) def update_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, retry=False): """ The shorter version of update_text_value(), which clears existing text and adds new text into the text field. We want to keep the old version for backward compatibility. """ self.update_text_value(selector, new_value, by=by, timeout=timeout, retry=retry) def is_element_present(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_present(self.driver, selector, by) def is_element_visible(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_visible(self.driver, selector, by) def is_link_text_visible(self, link_text): return page_actions.is_element_visible(self.driver, link_text, by=By.LINK_TEXT) def is_text_visible(self, text, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_text_visible(self.driver, text, selector, by) def find_visible_elements(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.find_visible_elements(self.driver, selector, by) def execute_script(self, script): return self.driver.execute_script(script) def set_window_size(self, width, height): return self.driver.set_window_size(width, height) self._demo_mode_pause_if_active() def maximize_window(self): return self.driver.maximize_window() self._demo_mode_pause_if_active() def activate_jquery(self): """ If "jQuery is not defined", use this method to activate it for use. This happens because jQuery is not always defined on web sites. """ try: # Let's first find out if jQuery is already defined. self.execute_script("jQuery('html')") # Since that command worked, jQuery is defined. Let's return. return except Exception: # jQuery is not currently defined. Let's proceed by defining it. pass self.execute_script( '''var script = document.createElement("script"); ''' '''script.src = "http://code.jquery.com/jquery-2.2.4.min.js"; ''' '''document.getElementsByTagName("head")[0]''' '''.appendChild(script);''') for x in xrange(30): # jQuery needs a small amount of time to activate. (At most 3s) try: self.execute_script("jQuery('html')") return except Exception: time.sleep(0.1) # Since jQuery still isn't activating, give up and raise an exception raise Exception("Exception: WebDriver could not activate jQuery!") def highlight(self, selector, by=By.CSS_SELECTOR, loops=4, scroll=True): """ This method uses fancy javascript to highlight an element. Used during demo_mode. @Params selector - the selector of the element to find by - the type of selector to search by (Default: CSS) loops - # of times to repeat the highlight animation (Default: 4) (4 loops is about 0.70 seconds. Your mileage may vary) scroll - the option to scroll to the element first (Default: True) """ element = self.find_element(selector, by=by, timeout=settings.SMALL_TIMEOUT) if scroll: self._slow_scroll_to_element(element) try: selector = self.convert_to_css_selector(selector, by=by) except Exception: # Don't highlight if can't convert to CSS_SELECTOR for jQuery return # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' o_bs = '' # original_box_shadow style = element.get_attribute('style') if style: if 'box-shadow: ' in style: box_start = style.find('box-shadow: ') box_end = style.find(';', box_start) + 1 original_box_shadow = style[box_start:box_end] o_bs = original_box_shadow script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 128, 128, 0.5)');""" % selector try: self.execute_script(script) except Exception: self.activate_jquery() self.execute_script(script) loops = int(loops) for n in xrange(loops): script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(255, 0, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(0, 0, 255, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(0, 255, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 128, 0, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '0px 0px 6px 6px rgba(128, 0, 128, 1)');""" % selector self.execute_script(script) time.sleep(0.02) script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs) self.execute_script(script) time.sleep(0.065) def scroll_to(self, selector, by=By.CSS_SELECTOR): ''' Fast scroll to destination ''' element = self.wait_for_element_visible(selector, by=by, timeout=settings.SMALL_TIMEOUT) self._scroll_to_element(element) def slow_scroll_to(self, selector, by=By.CSS_SELECTOR): ''' Slow motion scroll to destination ''' element = self.wait_for_element_visible(selector, by=by, timeout=settings.SMALL_TIMEOUT) self._slow_scroll_to_element(element) def scroll_click(self, selector, by=By.CSS_SELECTOR): self.scroll_to(selector, by=by) self.click(selector, by=by) def jquery_click(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH self.scroll_to(selector, by=by) selector = self.convert_to_css_selector(selector, by=by) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' click_script = """jQuery('%s').click()""" % selector try: self.execute_script(click_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(click_script) self._demo_mode_pause_if_active() def jq_format(self, code): return page_utils.jq_format(code) def get_domain_url(self, url): return page_utils.get_domain_url(url) def convert_xpath_to_css(self, xpath): return xpath_to_css.convert_xpath_to_css(xpath) def convert_to_css_selector(self, selector, by): """ This method converts a selector to a CSS_SELECTOR. jQuery commands require a CSS_SELECTOR for finding elements. This method should only be used for jQuery actions. """ if by == By.CSS_SELECTOR: return selector elif by == By.ID: return '#%s' % selector elif by == By.CLASS_NAME: return '.%s' % selector elif by == By.NAME: return '[name="%s"]' % selector elif by == By.TAG_NAME: return selector elif by == By.XPATH: return self.convert_xpath_to_css(selector) elif by == By.LINK_TEXT: return 'a:contains("%s")' % selector elif by == By.PARTIAL_LINK_TEXT: return 'a:contains("%s")' % selector else: raise Exception( "Exception: Could not convert [%s](by=%s) to CSS_SELECTOR!" % (selector, by)) def set_value(self, selector, value, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by) selector = self.convert_to_css_selector(selector, by=by) val = json.dumps(value) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' set_value_script = """jQuery('%s').val(%s)""" % (selector, val) try: self.execute_script(set_value_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(set_value_script) self._demo_mode_pause_if_active() def jquery_update_text_value(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = self.wait_for_element_visible(selector, by=by, timeout=timeout) self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by) selector = self.convert_to_css_selector(selector, by=by) # Only get the first match last_syllable = selector.split(' ')[-1] if ':' not in last_syllable: selector += ':first' update_text_script = """jQuery('%s').val('%s')""" % ( selector, self.jq_format(new_value)) try: self.execute_script(update_text_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.execute_script(update_text_script) if new_value.endswith('\n'): element.send_keys('\n') self._demo_mode_pause_if_active() def jquery_update_text(self, selector, new_value, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): self.jquery_update_text_value(selector, new_value, by=by, timeout=timeout) def hover_on_element(self, selector, by=By.CSS_SELECTOR): self.wait_for_element_visible(selector, by=by, timeout=settings.SMALL_TIMEOUT) self._demo_mode_highlight_if_active(selector, by) self.scroll_to(selector, by=by) time.sleep(0.05) # Settle down from scrolling before hovering return page_actions.hover_on_element(self.driver, selector) def hover_and_click(self, hover_selector, click_selector, hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if hover_selector.startswith('/') or hover_selector.startswith('./'): hover_by = By.XPATH if click_selector.startswith('/') or click_selector.startswith('./'): click_by = By.XPATH self.wait_for_element_visible(hover_selector, by=hover_by, timeout=timeout) self._demo_mode_highlight_if_active(hover_selector, hover_by) self.scroll_to(hover_selector, by=hover_by) pre_action_url = self.driver.current_url element = page_actions.hover_and_click(self.driver, hover_selector, click_selector, hover_by, click_by, timeout) if self.demo_mode: if self.driver.current_url != pre_action_url: self._demo_mode_pause_if_active() else: self._demo_mode_pause_if_active(tiny=True) return element ############ def wait_for_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to appear in the HTML of a page. The element does not need be visible (it may be hidden). """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_present(self.driver, selector, by, timeout) def assert_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_present(), but returns nothing. Waits for an element to appear in the HTML of a page. The element does not need be visible (it may be hidden). Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_present(selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to appear in the HTML of a page. The element must be visible (it cannot be hidden). """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_visible(self.driver, selector, by, timeout) def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_element_visible() """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def find_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_element_visible() - returns the element """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_visible(selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_text_visible(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_text_visible(self.driver, text, selector, by, timeout) def wait_for_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_text_visible() """ return self.wait_for_text_visible(text, selector, by=by, timeout=timeout) def find_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_text_visible() - returns the element """ return self.wait_for_text_visible(text, selector, by=by, timeout=timeout) def assert_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_text_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_text_visible(text, selector, by=by, timeout=timeout) return True # For backwards compatibility, earlier method names of the next # four methods have remained even though they do the same thing, # with the exception of assert_*, which won't return the element, # but like the others, will raise an exception if the call fails. def wait_for_link_text_visible(self, link_text, timeout=settings.LARGE_TIMEOUT): return self.wait_for_element_visible(link_text, by=By.LINK_TEXT, timeout=timeout) def wait_for_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_link_text_visible() """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def find_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_link_text_visible() - returns the element """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def assert_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_link_text_visible(), but returns nothing. As above, will raise an exception if nothing can be found. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_link_text_visible(link_text, timeout=timeout) return True ############ def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to no longer appear in the HTML of a page. A hidden element still counts as appearing in the page HTML. If an element with "hidden" status is acceptable, use wait_for_element_not_visible() instead. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_absent(self.driver, selector, by, timeout) def assert_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_absent() - returns nothing. As above, will raise an exception if the element stays present. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_absent(selector, by=by, timeout=timeout) return True ############ def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Waits for an element to no longer be visible on a page. The element can be non-existant in the HTML or hidden on the page to qualify as not visible. """ if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_not_visible( self.driver, selector, by, timeout) def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): """ Similar to wait_for_element_not_visible() - returns nothing. As above, will raise an exception if the element stays visible. Returns True if successful. Default timeout = SMALL_TIMEOUT. """ self.wait_for_element_not_visible(selector, by=by, timeout=timeout) return True ############ def wait_for_ready_state_complete(self, timeout=settings.EXTREME_TIMEOUT): return page_actions.wait_for_ready_state_complete(self.driver, timeout) def wait_for_and_accept_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_accept_alert(self.driver, timeout) def wait_for_and_dismiss_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_dismiss_alert(self.driver, timeout) def wait_for_and_switch_to_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_switch_to_alert(self.driver, timeout) def save_screenshot(self, name, folder=None): return page_actions.save_screenshot(self.driver, name, folder) ############ def _get_exception_message(self): """ This method extracts the message from an exception if there was an exception that occurred during the test, assuming that the exception was in a try/except block and not thrown. """ exception_info = sys.exc_info()[1] if hasattr(exception_info, 'msg'): exc_message = exception_info.msg elif hasattr(exception_info, 'message'): exc_message = exception_info.message else: exc_message = '(Unknown Exception)' return exc_message def _package_check(self): current_url = self.driver.current_url message = self._get_exception_message() self.page_check_failures.append( "CHECK #%s: (%s)\n %s" % (self.page_check_count, current_url, message)) def check_assert_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.TINY_TIMEOUT): """ A non-terminating assertion for an element on a page. Any and all exceptions will be saved until the process_checks() method is called from inside a test, likely at the end of it. """ self.page_check_count += 1 try: self.wait_for_element_visible(selector, by=by, timeout=timeout) return True except Exception: self._package_check() return False def check_assert_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.TINY_TIMEOUT): """ A non-terminating assertion for text from an element on a page. Any and all exceptions will be saved until the process_checks() method is called from inside a test, likely at the end of it. """ self.page_check_count += 1 try: self.wait_for_text_visible(text, selector, by=by, timeout=timeout) return True except Exception: self._package_check() return False def process_checks(self): """ To be used at the end of any test that uses checks, which are non-terminating verifications that will only raise an exception after this method is called. Useful for pages with multiple elements to be checked when you want to find as many failures as possible on a page before making fixes. Might be more useful if this method is called after processing all the checks for a single html page, otherwise the screenshot in the logs file won't match the location of the checks. """ if self.page_check_failures: exception_output = '' exception_output += "\n*** FAILED CHECKS FOR: %s\n" % self.id() all_failing_checks = self.page_check_failures self.page_check_failures = [] for tb in all_failing_checks: exception_output += "%s\n" % tb raise Exception(exception_output) ############ def _demo_mode_pause_if_active(self, tiny=False): if self.demo_mode: if self.demo_sleep: wait_time = float(self.demo_sleep) else: wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT if not tiny: time.sleep(wait_time) else: time.sleep(wait_time / 3.4) def _demo_mode_scroll_if_active(self, selector, by): if self.demo_mode: self.slow_scroll_to(selector, by=by) def _demo_mode_highlight_if_active(self, selector, by): if self.demo_mode: # Includes self.slow_scroll_to(selector, by=by) by default self.highlight(selector, by=by) def _scroll_to_element(self, element): element_location = element.location['y'] element_location = element_location - 130 if element_location < 0: element_location = 0 scroll_script = "window.scrollTo(0, %s);" % element_location # The old jQuery scroll_script required by=By.CSS_SELECTOR # scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector self.execute_script(scroll_script) self._demo_mode_pause_if_active(tiny=True) def _slow_scroll_to_element(self, element): scroll_position = self.execute_script("return window.scrollY;") element_location = element.location['y'] element_location = element_location - 130 if element_location < 0: element_location = 0 distance = element_location - scroll_position if distance != 0: total_steps = int(abs(distance) / 50.0) + 2.0 step_value = float(distance) / total_steps new_position = scroll_position for y in xrange(int(total_steps)): time.sleep(0.0114) new_position += step_value scroll_script = "window.scrollTo(0, %s);" % new_position self.execute_script(scroll_script) time.sleep(0.01) scroll_script = "window.scrollTo(0, %s);" % element_location self.execute_script(scroll_script) time.sleep(0.01) if distance > 430 or distance < -300: # Add small recovery time for long-distance slow-scrolling time.sleep(0.162) # PyTest-Specific Code # def setUp(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass setUp() method: super(SubClassOfBaseCase, self).setUp() """ self.is_pytest = None try: # This raises an exception if the test is not coming from pytest self.is_pytest = pytest.config.option.is_pytest except Exception: # Not using pytest (probably nosetests) self.is_pytest = False if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) self.with_selenium = pytest.config.option.with_selenium self.headless = pytest.config.option.headless self.headless_active = False self.with_testing_base = pytest.config.option.with_testing_base self.with_db_reporting = pytest.config.option.with_db_reporting self.with_s3_logging = pytest.config.option.with_s3_logging self.with_screen_shots = pytest.config.option.with_screen_shots self.with_basic_test_info = ( pytest.config.option.with_basic_test_info) self.with_page_source = pytest.config.option.with_page_source self.database_env = pytest.config.option.database_env self.log_path = pytest.config.option.log_path self.browser = pytest.config.option.browser self.data = pytest.config.option.data self.demo_mode = pytest.config.option.demo_mode self.demo_sleep = pytest.config.option.demo_sleep if self.with_db_reporting: self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False self.testcase_manager = TestcaseManager(self.database_env) # exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) # data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if self.with_selenium: data_payload.browser = self.browser else: data_payload.browser = "N/A" data_payload.testcaseAddress = test_id application = ApplicationManager.generate_application_string( self._testMethodName) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) if self.headless: self.display = Display(visible=0, size=(1200, 800)) self.display.start() self.headless_active = True if self.with_selenium: self.driver = browser_launcher.get_driver(self.browser) def __insert_test_result(self, state, err): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err: tb_string = traceback.format_exc() if "Message: " in tb_string: data_payload.message = "Message: " + tb_string.split( "Message: ")[-1] elif "Exception: " in tb_string: data_payload.message = tb_string.split("Exception: ")[-1] elif "Error: " in tb_string: data_payload.message = tb_string.split("Error: ")[-1] else: data_payload.message = "Unknown Error: See Stacktrace" self.testcase_manager.update_testcase_data(data_payload) def tearDown(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass's tearDown(): super(SubClassOfBaseCase, self).tearDown() """ if self.page_check_failures: # self.process_checks() was not called after checks were made. # We will log those now here, but without raising an exception. exception_output = '' exception_output += "\n*** FAILED CHECKS FOR: %s\n" % self.id() for tb in self.page_check_failures: exception_output += "%s\n" % tb logging.exception(exception_output) if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) if self.with_selenium: # Save a screenshot if logging is on when an exception occurs if self.with_testing_base and (sys.exc_info()[1] is not None): test_logpath = self.log_path + "/" + test_id if not os.path.exists(test_logpath): os.makedirs(test_logpath) if ((not self.with_screen_shots) and (not self.with_basic_test_info) and (not self.with_page_source)): # Log everything if nothing specified (if testing_base) log_helper.log_screenshot(test_logpath, self.driver) log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) log_helper.log_page_source(test_logpath, self.driver) else: if self.with_screen_shots: log_helper.log_screenshot(test_logpath, self.driver) if self.with_basic_test_info: log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) if self.with_page_source: log_helper.log_page_source(test_logpath, self.driver) # Finally close the browser self.driver.quit() if self.headless: if self.headless_active: self.display.stop() if self.with_db_reporting: if sys.exc_info()[1] is not None: self.__insert_test_result(constants.State.ERROR, True) else: self.__insert_test_result(constants.State.PASS, False) runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data( self.execution_guid, runtime) if self.with_s3_logging and (sys.exc_info()[1] is not None): """ After each testcase, upload logs to the S3 bucket. """ s3_bucket = S3LoggingBucket() guid = str(uuid.uuid4().hex) path = "%s/%s" % (self.log_path, test_id) uploaded_files = [] for logfile in os.listdir(path): logfile_name = "%s/%s/%s" % (guid, test_id, logfile.split(path)[-1]) s3_bucket.upload_file(logfile_name, "%s/%s" % (path, logfile)) uploaded_files.append(logfile_name) s3_bucket.save_uploaded_file_names(uploaded_files) index_file = s3_bucket.upload_index_file(test_id, guid) print "\n\n*** Log files uploaded: ***\n%s\n" % index_file logging.error("\n\n*** Log files uploaded: ***\n%s\n" % index_file) if self.with_db_reporting: self.testcase_manager = TestcaseManager(self.database_env) data_payload = TestcaseDataPayload() data_payload.guid = self.testcase_guid data_payload.logURL = index_file self.testcase_manager.update_testcase_log_url(data_payload)
class DBReporting(Plugin): """ The plugin for reporting test results in the database. """ name = 'db_reporting' # Usage: --with-db_reporting def __init__(self): Plugin.__init__(self) self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False def options(self, parser, env): super(DBReporting, self).options(parser, env=env) parser.add_option('--database_environment', action='store', dest='database_env', choices=('prod', 'qa', 'test'), default='test', help=SUPPRESS_HELP) def configure(self, options, conf): super(DBReporting, self).configure(options, conf) self.options = options self.testcase_manager = TestcaseManager(self.options.database_env) def begin(self): """At the start of the run, we want to record the test execution information in the database.""" exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) def startTest(self, test): """At the start of the test, set the testcase details.""" data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if hasattr(test, "browser"): data_payload.browser = test.browser else: data_payload.browser = "N/A" data_payload.testcaseAddress = test.id() application = ApplicationManager.generate_application_string(test) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) # Make the testcase guid available to other plugins test.testcase_guid = self.testcase_guid def finalize(self, result): """At the end of the run, we want to update the DB row with the execution time.""" runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data(self.execution_guid, runtime) def addSuccess(self, test, capt): """ After test completion, we want to record testcase run information. """ self.__insert_test_result(constants.State.PASS, test) def addError(self, test, err, capt=None): """ After a test error, we want to record testcase run information. """ self.__insert_test_result(constants.State.ERROR, test, err) def handleError(self, test, err, capt=None): """ After a test error, we want to record testcase run information. "Error" also encompasses any states other than Pass or Fail, so we check for those first. """ if err[0] == errors.BlockedTest: self.__insert_test_result(constants.State.BLOCKED, test, err) self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.DeprecatedTest: self.__insert_test_result(constants.State.DEPRECATED, test, err) self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.SkipTest: self.__insert_test_result(constants.State.SKIP, test, err) self.error_handled = True raise SkipTest(err[1]) return True def addFailure(self, test, err, capt=None, tbinfo=None): """ After a test failure, we want to record testcase run information. """ self.__insert_test_result(constants.State.FAILURE, test, err) def __insert_test_result(self, state, test, err=None): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err is not None: data_payload.message = err[1].__str__().split( '''-------------------- >> ''' '''begin captured logging''' ''' << --------------------''', 1)[0] self.testcase_manager.update_testcase_data(data_payload)
class BaseCase(unittest.TestCase): ''' A base test case that wraps methods for enhanced usage. You can also add your own methods here. ''' def __init__(self, *args, **kwargs): super(BaseCase, self).__init__(*args, **kwargs) try: self.driver = WebDriver() except Exception: pass self.environment = None def open(self, url): self.driver.get(url) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def open_url(self, url): """ In case people are mixing up self.open() with open(), use this alternative. """ self.open(url) def click(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout) self._demo_mode_scroll_if_active(selector, by) element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def click_chain(self, selectors_list, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, spacing=0): """ This method clicks on a list of elements in succession. 'spacing' is the amount of time to wait between clicks. (sec) """ for selector in selectors_list: self.click(selector, by=by, timeout=timeout) if spacing > 0: time.sleep(spacing) def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): element = self.wait_for_link_text_visible(link_text, timeout=timeout) element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def add_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): """ The more-reliable version of driver.send_keys() Similar to update_text(), but won't clear the text field first. """ element = self.wait_for_element_visible(selector, timeout=timeout) element.send_keys(new_value) self._demo_mode_pause_if_active() def send_keys(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): """ Same as add_text() -> more reliable, but less name confusion. """ self.add_text(selector, new_value, timeout=timeout) def update_text_value(self, selector, new_value, timeout=settings.SMALL_TIMEOUT, retry=False): """ This method updates an element's text value with a new value. @Params selector - the selector with the value to update new_value - the new value for setting the text field timeout - how long to wait for the selector to be visible retry - if True, use jquery if the selenium text update fails """ element = self.wait_for_element_visible(selector, timeout=timeout) element.clear() self._demo_mode_pause_if_active(tiny=True) element.send_keys(new_value) if (retry and element.get_attribute('value') != new_value and ( not new_value.endswith('\n'))): logging.debug('update_text_value is falling back to jQuery!') selector = self.jq_format(selector) self.set_value(selector, new_value) self._demo_mode_pause_if_active() def update_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT, retry=False): """ The shorter version of update_text_value(), which clears existing text and adds new text into the text field. We want to keep the old version for backward compatibility. """ self.update_text_value(selector, new_value, timeout=timeout, retry=retry) def is_element_present(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_present(self.driver, selector, by) def is_element_visible(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_visible(self.driver, selector, by) def is_link_text_visible(self, link_text): return page_actions.is_element_visible(self.driver, link_text, by=By.LINK_TEXT) def is_text_visible(self, text, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_text_visible(self.driver, text, selector, by) def find_visible_elements(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.find_visible_elements(self.driver, selector, by) def execute_script(self, script): return self.driver.execute_script(script) def set_window_size(self, width, height): return self.driver.set_window_size(width, height) self._demo_mode_pause_if_active() def maximize_window(self): return self.driver.maximize_window() self._demo_mode_pause_if_active() def activate_jquery(self): """ If "jQuery is not defined", use this method to activate it for use. This happens because jQuery is not always defined on web sites. """ try: # Let's first find out if jQuery is already defined. self.driver.execute_script("jQuery('html')") # Since that command worked, jQuery is defined. Let's return. return except Exception: # jQuery is not currently defined. Let's proceed by defining it. pass self.driver.execute_script( '''var script = document.createElement("script"); ''' '''script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1/''' '''jquery.min.js"; document.getElementsByTagName("head")[0]''' '''.appendChild(script);''') for x in xrange(30): # jQuery needs a small amount of time to activate. (At most 3s) try: self.driver.execute_script("jQuery('html')") return except Exception: time.sleep(0.1) # Since jQuery still isn't activating, give up and raise an exception raise Exception("Exception: WebDriver could not activate jQuery!") def scroll_to(self, selector, wait=True): if wait: # Fail here if element isn't visible after SMALL_TIMEOUT seconds self.wait_for_element_visible( selector, timeout=settings.SMALL_TIMEOUT) else: # Might be a jQuery action to interact with an invisible element if not self.is_element_visible: time.sleep(0.05) # Last chance to load before scrolling there scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector try: self.driver.execute_script(scroll_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.driver.execute_script(scroll_script) self._demo_mode_pause_if_active(tiny=True) def scroll_click(self, selector): self.scroll_to(selector) self.click(selector) def jquery_click(self, selector, wait=False): self.scroll_to(selector, wait=wait) self.driver.execute_script("jQuery('%s').click()" % selector) self._demo_mode_pause_if_active() def jq_format(self, code): return page_utils.jq_format(code) def set_value(self, selector, value, wait=False): self.scroll_to(selector, wait=wait) val = json.dumps(value) self.driver.execute_script("jQuery('%s').val(%s)" % (selector, val)) self._demo_mode_pause_if_active() def jquery_update_text_value(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): element = self.wait_for_element_visible(selector, timeout=timeout) self.scroll_to(selector) self.driver.execute_script("""jQuery('%s').val('%s')""" % (selector, self.jq_format(new_value))) if new_value.endswith('\n'): element.send_keys('\n') self._demo_mode_pause_if_active() def jquery_update_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): self.jquery_update_text_value(selector, new_value, timeout=timeout) def hover_on_element(self, selector): self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT) self.scroll_to(selector) time.sleep(0.05) # Settle down from scrolling before hovering return page_actions.hover_on_element(self.driver, selector) def hover_and_click(self, hover_selector, click_selector, click_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if click_selector.startswith('/'): click_by = By.XPATH self.wait_for_element_visible(hover_selector, timeout=timeout) self.scroll_to(hover_selector) # Settle down from the scrolling before hovering element = page_actions.hover_and_click( self.driver, hover_selector, click_selector, click_by, timeout) self._demo_mode_pause_if_active() return element def wait_for_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_present( self.driver, selector, by, timeout) def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_visible( self.driver, selector, by, timeout) def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_element_visible() """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def wait_for_text_visible(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_text_visible( self.driver, text, selector, by, timeout) def wait_for_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_text_visible() """ return self.wait_for_text_visible( text, selector, by=by, timeout=timeout) def wait_for_link_text_visible(self, link_text, timeout=settings.LARGE_TIMEOUT): return self.wait_for_element_visible( link_text, by=By.LINK_TEXT, timeout=timeout) def wait_for_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_link_text_visible() """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_absent( self.driver, selector, by, timeout) def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_not_visible( self.driver, selector, by, timeout) def wait_for_ready_state_complete(self, timeout=settings.EXTREME_TIMEOUT): return page_actions.wait_for_ready_state_complete(self.driver, timeout) def wait_for_and_accept_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_accept_alert(self.driver, timeout) def wait_for_and_dismiss_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_dismiss_alert(self.driver, timeout) def wait_for_and_switch_to_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_switch_to_alert(self.driver, timeout) def save_screenshot(self, name, folder=None): return page_actions.save_screenshot(self.driver, name, folder) def _demo_mode_pause_if_active(self, tiny=False): if self.demo_mode: if self.demo_sleep: wait_time = float(self.demo_sleep) else: wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT if not tiny: time.sleep(wait_time) else: time.sleep(wait_time/3.0) def _demo_mode_scroll_if_active(self, selector, by): if self.demo_mode: if by == By.CSS_SELECTOR: self.scroll_to(selector) # PyTest-Specific Code # def setUp(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass setUp() method: super(SubClassOfBaseCase, self).setUp() """ self.is_pytest = None try: # This raises an exception if the test is not coming from pytest self.is_pytest = pytest.config.option.is_pytest except Exception: # Not using pytest (probably nosetests) self.is_pytest = False if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) self.with_selenium = pytest.config.option.with_selenium self.headless = pytest.config.option.headless self.headless_active = False self.with_testing_base = pytest.config.option.with_testing_base self.with_db_reporting = pytest.config.option.with_db_reporting self.with_s3_logging = pytest.config.option.with_s3_logging self.with_screen_shots = pytest.config.option.with_screen_shots self.with_basic_test_info = ( pytest.config.option.with_basic_test_info) self.with_page_source = pytest.config.option.with_page_source self.database_env = pytest.config.option.database_env self.log_path = pytest.config.option.log_path self.browser = pytest.config.option.browser self.data = pytest.config.option.data self.demo_mode = pytest.config.option.demo_mode self.demo_sleep = pytest.config.option.demo_sleep if self.with_db_reporting: self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False self.testcase_manager = TestcaseManager(self.database_env) # exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) # data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if self.with_selenium: data_payload.browser = self.browser else: data_payload.browser = "N/A" data_payload.testcaseAddress = test_id application = ApplicationManager.generate_application_string( self._testMethodName) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) if self.headless: self.display = Display(visible=0, size=(1200, 800)) self.display.start() self.headless_active = True if self.with_selenium: self.driver = browser_launcher.get_driver(self.browser) def __insert_test_result(self, state, err): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err: tb_string = traceback.format_exc() if "Message: " in tb_string: data_payload.message = "Message: " + tb_string.split( "Message: ")[-1] elif "Exception: " in tb_string: data_payload.message = tb_string.split("Exception: ")[-1] elif "Error: " in tb_string: data_payload.message = tb_string.split("Error: ")[-1] else: data_payload.message = "Unknown Error: See Stacktrace" self.testcase_manager.update_testcase_data(data_payload) def tearDown(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass's tearDown(): super(SubClassOfBaseCase, self).tearDown() """ if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) if self.with_selenium: # Save a screenshot if logging is on when an exception occurs if self.with_testing_base and (sys.exc_info()[1] is not None): test_logpath = self.log_path + "/" + test_id if not os.path.exists(test_logpath): os.makedirs(test_logpath) if ((not self.with_screen_shots) and (not self.with_basic_test_info) and (not self.with_page_source)): # Log everything if nothing specified (if testing_base) log_helper.log_screenshot(test_logpath, self.driver) log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) log_helper.log_page_source(test_logpath, self.driver) else: if self.with_screen_shots: log_helper.log_screenshot( test_logpath, self.driver) if self.with_basic_test_info: log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) if self.with_page_source: log_helper.log_page_source( test_logpath, self.driver) # Finally close the browser self.driver.quit() if self.headless: if self.headless_active: self.display.stop() if self.with_db_reporting: if sys.exc_info()[1] is not None: self.__insert_test_result(constants.State.ERROR, True) else: self.__insert_test_result(constants.State.PASS, False) runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data( self.execution_guid, runtime) if self.with_s3_logging and (sys.exc_info()[1] is not None): """ After each testcase, upload logs to the S3 bucket. """ s3_bucket = S3LoggingBucket() guid = str(uuid.uuid4().hex) path = "%s/%s" % (self.log_path, test_id) uploaded_files = [] for logfile in os.listdir(path): logfile_name = "%s/%s/%s" % (guid, test_id, logfile.split(path)[-1]) s3_bucket.upload_file(logfile_name, "%s/%s" % (path, logfile)) uploaded_files.append(logfile_name) s3_bucket.save_uploaded_file_names(uploaded_files) index_file = s3_bucket.upload_index_file(test_id, guid) print "\n\n*** Log files uploaded: ***\n%s\n" % index_file logging.error( "\n\n*** Log files uploaded: ***\n%s\n" % index_file) if self.with_db_reporting: self.testcase_manager = TestcaseManager(self.database_env) data_payload = TestcaseDataPayload() data_payload.guid = self.testcase_guid data_payload.logURL = index_file self.testcase_manager.update_testcase_log_url(data_payload)
class DBReporting(Plugin): """ This plugin records test results in the Testcase Database. """ name = "db_reporting" # Usage: --with-db_reporting def __init__(self): Plugin.__init__(self) self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False self._result_set = False self._test = None def options(self, parser, env): super(DBReporting, self).options(parser, env=env) parser.add_option( "--database_env", "--database-env", action="store", dest="database_env", choices=( constants.Environment.QA, constants.Environment.STAGING, constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.MASTER, constants.Environment.REMOTE, constants.Environment.LOCAL, constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.MAIN, constants.Environment.TEST, ), default=constants.Environment.TEST, help="The database environment to run the tests in.", ) def configure(self, options, conf): super(DBReporting, self).configure(options, conf) self.options = options self.testcase_manager = TestcaseManager(self.options.database_env) def begin(self): """At the start of the run, we want to record the test execution information in the database.""" exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) def startTest(self, test): """At the start of the test, set the testcase details.""" data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if hasattr(test, "browser"): data_payload.browser = test.browser else: data_payload.browser = "N/A" data_payload.test_address = test.id() application = ApplicationManager.generate_application_string(test) data_payload.env = application.split(".")[0] data_payload.start_time = application.split(".")[1] data_payload.state = constants.State.UNTESTED self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) # Make the testcase guid available to other plugins test.testcase_guid = self.testcase_guid self._test = test self._test._nose_skip_reason = None def finalize(self, result): """At the end of the test run, we want to update the DB row with the total execution time.""" runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data(self.execution_guid, runtime) def afterTest(self, test): if not self._result_set: err = None try: err = self._test._nose_skip_reason if err: err = "Skipped: " + str(err) err = (err, err) except Exception: pass if not err: err = "Skipped: (no reason given)" err = (err, err) self.__insert_test_result(constants.State.SKIPPED, self._test, err) def addSuccess(self, test, capt): """ After each test success, record testcase run information. """ self.__insert_test_result(constants.State.PASSED, test) self._result_set = True def addFailure(self, test, err, capt=None, tbinfo=None): """ After each test failure, record testcase run information. """ self.__insert_test_result(constants.State.FAILED, test, err) self._result_set = True def addError(self, test, err, capt=None): """ After each test error, record testcase run information. (Test errors should be treated the same as test failures.) """ self.__insert_test_result(constants.State.FAILED, test, err) self._result_set = True def handleError(self, test, err, capt=None): """ After each test error, record testcase run information. "Error" also encompasses any states other than Pass or Fail, so we check for those first. """ if err[0] == errors.BlockedTest: self.__insert_test_result(constants.State.BLOCKED, test, err) self._result_set = True self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.DeprecatedTest: self.__insert_test_result(constants.State.DEPRECATED, test, err) self._result_set = True self.error_handled = True raise SkipTest(err[1]) return True elif err[0] == errors.SkipTest: self.__insert_test_result(constants.State.SKIPPED, test, err) self._result_set = True self.error_handled = True raise SkipTest(err[1]) return True def __insert_test_result(self, state, test, err=None): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err is not None: data_payload.message = (err[1].__str__().split( """-------------------- >> """ """begin captured logging""" """ << --------------------""", 1, )[0]) self.testcase_manager.update_testcase_data(data_payload)
class BaseCase(unittest.TestCase): ''' A base test case that wraps methods for enhanced usage. You can also add your own methods here. ''' def __init__(self, *args, **kwargs): super(BaseCase, self).__init__(*args, **kwargs) try: self.driver = WebDriver() except Exception: pass self.environment = None def open(self, url): self.driver.get(url) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def open_url(self, url): """ In case people are mixing up self.open() with open(), use this alternative. """ self.open(url) def click(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH element = page_actions.wait_for_element_visible(self.driver, selector, by, timeout=timeout) self._demo_mode_scroll_if_active(selector, by) element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def click_chain(self, selectors_list, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT, spacing=0): """ This method clicks on a list of elements in succession. 'spacing' is the amount of time to wait between clicks. (sec) """ for selector in selectors_list: self.click(selector, by=by, timeout=timeout) if spacing > 0: time.sleep(spacing) def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT): element = self.wait_for_link_text_visible(link_text, timeout=timeout) element.click() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() self._demo_mode_pause_if_active() def get_text(self, selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout) return element.text def add_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): """ The more-reliable version of driver.send_keys() Similar to update_text(), but won't clear the text field first. """ element = self.wait_for_element_visible(selector, timeout=timeout) element.send_keys(new_value) self._demo_mode_pause_if_active() def send_keys(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): """ Same as add_text() -> more reliable, but less name confusion. """ self.add_text(selector, new_value, timeout=timeout) def update_text_value(self, selector, new_value, timeout=settings.SMALL_TIMEOUT, retry=False): """ This method updates an element's text value with a new value. @Params selector - the selector with the value to update new_value - the new value for setting the text field timeout - how long to wait for the selector to be visible retry - if True, use jquery if the selenium text update fails """ element = self.wait_for_element_visible(selector, timeout=timeout) element.clear() self._demo_mode_pause_if_active(tiny=True) element.send_keys(new_value) if (retry and element.get_attribute('value') != new_value and (not new_value.endswith('\n'))): logging.debug('update_text_value is falling back to jQuery!') selector = self.jq_format(selector) self.set_value(selector, new_value) self._demo_mode_pause_if_active() def update_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT, retry=False): """ The shorter version of update_text_value(), which clears existing text and adds new text into the text field. We want to keep the old version for backward compatibility. """ self.update_text_value(selector, new_value, timeout=timeout, retry=retry) def is_element_present(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_present(self.driver, selector, by) def is_element_visible(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_element_visible(self.driver, selector, by) def is_link_text_visible(self, link_text): return page_actions.is_element_visible(self.driver, link_text, by=By.LINK_TEXT) def is_text_visible(self, text, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.is_text_visible(self.driver, text, selector, by) def find_visible_elements(self, selector, by=By.CSS_SELECTOR): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.find_visible_elements(self.driver, selector, by) def execute_script(self, script): return self.driver.execute_script(script) def set_window_size(self, width, height): return self.driver.set_window_size(width, height) self._demo_mode_pause_if_active() def maximize_window(self): return self.driver.maximize_window() self._demo_mode_pause_if_active() def activate_jquery(self): """ If "jQuery is not defined", use this method to activate it for use. This happens because jQuery is not always defined on web sites. """ try: # Let's first find out if jQuery is already defined. self.driver.execute_script("jQuery('html')") # Since that command worked, jQuery is defined. Let's return. return except Exception: # jQuery is not currently defined. Let's proceed by defining it. pass self.driver.execute_script( '''var script = document.createElement("script"); ''' '''script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1/''' '''jquery.min.js"; document.getElementsByTagName("head")[0]''' '''.appendChild(script);''') for x in xrange(30): # jQuery needs a small amount of time to activate. (At most 3s) try: self.driver.execute_script("jQuery('html')") return except Exception: time.sleep(0.1) # Since jQuery still isn't activating, give up and raise an exception raise Exception("Exception: WebDriver could not activate jQuery!") def scroll_to(self, selector, wait=True): if wait: # Fail here if element isn't visible after SMALL_TIMEOUT seconds self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT) else: # Might be a jQuery action to interact with an invisible element if not self.is_element_visible: time.sleep(0.05) # Last chance to load before scrolling there scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector try: self.driver.execute_script(scroll_script) except Exception: # The likely reason this fails is because: "jQuery is not defined" self.activate_jquery() # It's a good thing we can define it here self.driver.execute_script(scroll_script) self._demo_mode_pause_if_active(tiny=True) def scroll_click(self, selector): self.scroll_to(selector) self.click(selector) def jquery_click(self, selector, wait=False): self.scroll_to(selector, wait=wait) self.driver.execute_script("jQuery('%s').click()" % selector) self._demo_mode_pause_if_active() def jq_format(self, code): return page_utils.jq_format(code) def set_value(self, selector, value, wait=False): self.scroll_to(selector, wait=wait) val = json.dumps(value) self.driver.execute_script("jQuery('%s').val(%s)" % (selector, val)) self._demo_mode_pause_if_active() def jquery_update_text_value(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): element = self.wait_for_element_visible(selector, timeout=timeout) self.scroll_to(selector) self.driver.execute_script("""jQuery('%s').val('%s')""" % (selector, self.jq_format(new_value))) if new_value.endswith('\n'): element.send_keys('\n') self._demo_mode_pause_if_active() def jquery_update_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT): self.jquery_update_text_value(selector, new_value, timeout=timeout) def hover_on_element(self, selector): self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT) self.scroll_to(selector) time.sleep(0.05) # Settle down from scrolling before hovering return page_actions.hover_on_element(self.driver, selector) def hover_and_click(self, hover_selector, click_selector, click_by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT): if click_selector.startswith('/'): click_by = By.XPATH self.wait_for_element_visible(hover_selector, timeout=timeout) self.scroll_to(hover_selector) # Settle down from the scrolling before hovering element = page_actions.hover_and_click(self.driver, hover_selector, click_selector, click_by, timeout) self._demo_mode_pause_if_active() return element def wait_for_element_present(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_present(self.driver, selector, by, timeout) def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_visible(self.driver, selector, by, timeout) def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_element_visible() """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def find_element(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_element_visible() """ return self.wait_for_element_visible(selector, by=by, timeout=timeout) def wait_for_text_visible(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_text_visible(self.driver, text, selector, by, timeout) def wait_for_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_text_visible() """ return self.wait_for_text_visible(text, selector, by=by, timeout=timeout) def find_text(self, text, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_text_visible() """ return self.wait_for_text_visible(text, selector, by=by, timeout=timeout) def wait_for_link_text_visible(self, link_text, timeout=settings.LARGE_TIMEOUT): return self.wait_for_element_visible(link_text, by=By.LINK_TEXT, timeout=timeout) def wait_for_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ The shorter version of wait_for_link_text_visible() """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def find_link_text(self, link_text, timeout=settings.LARGE_TIMEOUT): """ Same as wait_for_link_text_visible() """ return self.wait_for_link_text_visible(link_text, timeout=timeout) def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_absent(self.driver, selector, by, timeout) def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR, timeout=settings.LARGE_TIMEOUT): if selector.startswith('/') or selector.startswith('./'): by = By.XPATH return page_actions.wait_for_element_not_visible( self.driver, selector, by, timeout) def wait_for_ready_state_complete(self, timeout=settings.EXTREME_TIMEOUT): return page_actions.wait_for_ready_state_complete(self.driver, timeout) def wait_for_and_accept_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_accept_alert(self.driver, timeout) def wait_for_and_dismiss_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_dismiss_alert(self.driver, timeout) def wait_for_and_switch_to_alert(self, timeout=settings.LARGE_TIMEOUT): return page_actions.wait_for_and_switch_to_alert(self.driver, timeout) def save_screenshot(self, name, folder=None): return page_actions.save_screenshot(self.driver, name, folder) def _demo_mode_pause_if_active(self, tiny=False): if self.demo_mode: if self.demo_sleep: wait_time = float(self.demo_sleep) else: wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT if not tiny: time.sleep(wait_time) else: time.sleep(wait_time / 3.0) def _demo_mode_scroll_if_active(self, selector, by): if self.demo_mode: if by == By.CSS_SELECTOR: self.scroll_to(selector) # PyTest-Specific Code # def setUp(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass setUp() method: super(SubClassOfBaseCase, self).setUp() """ self.is_pytest = None try: # This raises an exception if the test is not coming from pytest self.is_pytest = pytest.config.option.is_pytest except Exception: # Not using pytest (probably nosetests) self.is_pytest = False if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) self.with_selenium = pytest.config.option.with_selenium self.headless = pytest.config.option.headless self.headless_active = False self.with_testing_base = pytest.config.option.with_testing_base self.with_db_reporting = pytest.config.option.with_db_reporting self.with_s3_logging = pytest.config.option.with_s3_logging self.with_screen_shots = pytest.config.option.with_screen_shots self.with_basic_test_info = ( pytest.config.option.with_basic_test_info) self.with_page_source = pytest.config.option.with_page_source self.database_env = pytest.config.option.database_env self.log_path = pytest.config.option.log_path self.browser = pytest.config.option.browser self.data = pytest.config.option.data self.demo_mode = pytest.config.option.demo_mode self.demo_sleep = pytest.config.option.demo_sleep if self.with_db_reporting: self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.application = None self.testcase_manager = None self.error_handled = False self.testcase_manager = TestcaseManager(self.database_env) # exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) # data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if self.with_selenium: data_payload.browser = self.browser else: data_payload.browser = "N/A" data_payload.testcaseAddress = test_id application = ApplicationManager.generate_application_string( self._testMethodName) data_payload.env = application.split('.')[0] data_payload.start_time = application.split('.')[1] data_payload.state = constants.State.NOTRUN self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) if self.headless: self.display = Display(visible=0, size=(1200, 800)) self.display.start() self.headless_active = True if self.with_selenium: self.driver = browser_launcher.get_driver(self.browser) def __insert_test_result(self, state, err): data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err: tb_string = traceback.format_exc() if "Message: " in tb_string: data_payload.message = "Message: " + tb_string.split( "Message: ")[-1] elif "Exception: " in tb_string: data_payload.message = tb_string.split("Exception: ")[-1] elif "Error: " in tb_string: data_payload.message = tb_string.split("Error: ")[-1] else: data_payload.message = "Unknown Error: See Stacktrace" self.testcase_manager.update_testcase_data(data_payload) def tearDown(self): """ pytest-specific code Be careful if a subclass of BaseCase overrides setUp() You'll need to add the following line to the subclass's tearDown(): super(SubClassOfBaseCase, self).tearDown() """ if self.is_pytest: test_id = "%s.%s.%s" % (self.__class__.__module__, self.__class__.__name__, self._testMethodName) if self.with_selenium: # Save a screenshot if logging is on when an exception occurs if self.with_testing_base and (sys.exc_info()[1] is not None): test_logpath = self.log_path + "/" + test_id if not os.path.exists(test_logpath): os.makedirs(test_logpath) if ((not self.with_screen_shots) and (not self.with_basic_test_info) and (not self.with_page_source)): # Log everything if nothing specified (if testing_base) log_helper.log_screenshot(test_logpath, self.driver) log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) log_helper.log_page_source(test_logpath, self.driver) else: if self.with_screen_shots: log_helper.log_screenshot(test_logpath, self.driver) if self.with_basic_test_info: log_helper.log_test_failure_data( test_logpath, self.driver, self.browser) if self.with_page_source: log_helper.log_page_source(test_logpath, self.driver) # Finally close the browser self.driver.quit() if self.headless: if self.headless_active: self.display.stop() if self.with_db_reporting: if sys.exc_info()[1] is not None: self.__insert_test_result(constants.State.ERROR, True) else: self.__insert_test_result(constants.State.PASS, False) runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data( self.execution_guid, runtime) if self.with_s3_logging and (sys.exc_info()[1] is not None): """ After each testcase, upload logs to the S3 bucket. """ s3_bucket = S3LoggingBucket() guid = str(uuid.uuid4().hex) path = "%s/%s" % (self.log_path, test_id) uploaded_files = [] for logfile in os.listdir(path): logfile_name = "%s/%s/%s" % (guid, test_id, logfile.split(path)[-1]) s3_bucket.upload_file(logfile_name, "%s/%s" % (path, logfile)) uploaded_files.append(logfile_name) s3_bucket.save_uploaded_file_names(uploaded_files) index_file = s3_bucket.upload_index_file(test_id, guid) print "\n\n*** Log files uploaded: ***\n%s\n" % index_file logging.error("\n\n*** Log files uploaded: ***\n%s\n" % index_file) if self.with_db_reporting: self.testcase_manager = TestcaseManager(self.database_env) data_payload = TestcaseDataPayload() data_payload.guid = self.testcase_guid data_payload.logURL = index_file self.testcase_manager.update_testcase_log_url(data_payload)