Пример #1
0
    def afterTest(self, test):
        """ After each testcase, upload logs to the S3 bucket. """
        s3_bucket = S3LoggingBucket()
        guid = str(uuid.uuid4().hex)
        path = "%s/%s" % (self.options.log_path,
                          test.test.id())
        uploaded_files = []
        for logfile in os.listdir(path):
            logfile_name = "%s/%s/%s" % (guid,
                                         test.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 the database plugin is running, attach a link
        # to the logs index database row
        if hasattr(test.test, "testcase_guid"):
            from seleniumbase.core.testcase_manager \
                import TestcaseDataPayload, TestcaseManager
            self.testcase_manager = TestcaseManager(self.options.database_env)
            data_payload = TestcaseDataPayload()
            data_payload.guid = test.test.testcase_guid
            data_payload.logURL = index_file
            self.testcase_manager.update_testcase_log_url(data_payload)
class S3Logging(Plugin):
    """
    The plugin for uploading test logs to the S3 bucket specified.
    """
    name = 's3_logging'  # Usage: --with-s3_logging

    def configure(self, options, conf):
        """ Get the options. """
        super(S3Logging, self).configure(options, conf)
        self.options = options

    def afterTest(self, test):
        """ After each testcase, upload logs to the S3 bucket. """
        s3_bucket = S3LoggingBucket()
        guid = str(uuid.uuid4().hex)
        path = "%s/%s" % (self.options.log_path,
                          test.test.id())
        uploaded_files = []
        for logfile in os.listdir(path):
            logfile_name = "%s/%s/%s" % (guid,
                                         test.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 "Log files uploaded: %s" % index_file
        logging.error("Log files uploaded: %s" % index_file)

        # If the database plugin is running, attach a link
        # to the logs index database row
        if hasattr(test.test, "testcase_guid"):
            from seleniumbase.core.testcase_manager \
                import TestcaseDataPayload, TestcaseManager
            self.testcase_manager = TestcaseManager(self.options.database_env)
            data_payload = TestcaseDataPayload()
            data_payload.guid = test.test.testcase_guid
            data_payload.logURL = index_file
            self.testcase_manager.update_testcase_log_url(data_payload)
Пример #3
0
 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)
Пример #4
0
 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)
Пример #5
0
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)
Пример #6
0
 def configure(self, options, conf):
     super(DBReporting, self).configure(options, conf)
     self.options = options
     self.testcase_manager = TestcaseManager(self.options.database_env)
Пример #7
0
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)
Пример #8
0
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)
Пример #9
0
 def configure(self, options, conf):
     super(DBReporting, self).configure(options, conf)
     self.options = options
     self.testcase_manager = TestcaseManager(self.options.database_env)
Пример #10
0
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', 'staging', '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.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)
Пример #11
0
 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
         self.highlights = pytest.config.option.highlights
         self.verify_delay = pytest.config.option.verify_delay
         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)
Пример #12
0
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-3.1.0.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=settings.HIGHLIGHTS,
                  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. Each loop lasts for about 0.18s)
            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)
        if self.highlights:
            loops = self.highlights
        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)

    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)

    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
            self.highlights = pytest.config.option.highlights
            self.verify_delay = pytest.config.option.verify_delay
            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)
        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)
            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)
Пример #13
0
 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)
     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)
         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)
Пример #15
0
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)