예제 #1
0
 def check_region_in_frame_by_selector(self, frame_reference, by, value, tag=None,
                                       match_timeout=-1):
     """
     Checks a region within a frame, and returns to the current frame.
     Args:
         :param frame_reference: (int/str/WebElement) A reference to the frame in which the region should be checked.
         :param by: (By) The way by which an element to be validated should be found (e.g., By.ID).
         :param value: (str) The value identifying the element using the "by" type.
         :param tag: (str) Description of the visual validation checkpoint.
         :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
     Returns:
         None
     """
     if self.is_disabled:
         logger.info('check_region_in_frame_by_selector(): ignored (disabled)')
         return
     logger.info("check_region_in_frame_by_selector('%s')" % tag)
     # We need to temporarily save the hide_scrollbars value, since we'll change it to make sure that hide_scrollbars
     # will NOT be called twice (once outside the frame and once inside the frame).
     original_hide_scrollbars_value = self.hide_scrollbars
     if self.hide_scrollbars:
         original_overflow = self._driver.hide_scrollbars()
         self.hide_scrollbars = False
     # Switching to the relevant frame
     self._driver.switch_to.frame(frame_reference)
     logger.debug("calling 'check_region_by_selector'...")
     self.check_region_by_selector(by, value, tag, match_timeout)
     # Switching back to our original frame
     self._driver.switch_to.parent_frame()
     if original_hide_scrollbars_value:
         # noinspection PyUnboundLocalVariable
         self._driver.set_overflow(original_overflow)
         self.hide_scrollbars = original_hide_scrollbars_value
예제 #2
0
    def add_text_trigger_by_element(self, element, text):
        """
        Adds a text trigger.

        :param element: The element to which the text was sent.
        :param text: The trigger's text.
        """
        if self.is_disabled:
            logger.debug("add_text_trigger: Ignoring '%s' (disabled)" % text)
            return
        # Triggers are activated on the last checked window.
        if self._last_screenshot is None:
            logger.debug("add_text_trigger: Ignoring '%s' (no screenshot)" % text)
            return
        if not EyesFrame.is_same_frame_chain(self._driver.get_frame_chain(),
                                             self._last_screenshot.get_frame_chain()):
            logger.debug("add_text_trigger: Ignoring %s (different frame)" % text)
            return
        control = self._last_screenshot.get_intersected_region_by_element(element)
        # Making sure the trigger is within the last screenshot bounds
        if control.is_empty():
            logger.debug("add_text_trigger: Ignoring %s (out of bounds)" % text)
            return
        trigger = TextTrigger(control, text)
        self._user_inputs.append(trigger)
        logger.info("add_text_trigger: Added %s" % trigger)
예제 #3
0
    def check_region_in_frame_by_selector(self, frame_reference, by, value, tag=None,
                                          match_timeout=-1):
        """
        Checks a region within a frame, and returns to the current frame.

        :param frame_reference: (int/str/WebElement) A reference to the frame in which the region should be checked.
        :param by: (By) The way by which an element to be validated should be found (e.g., By.ID).
        :param value: (str) The value identifying the element using the "by" type.
        :param tag: (str) Description of the visual validation checkpoint.
        :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        :return: None
        """
        if self.is_disabled:
            logger.info('check_region_in_frame_by_selector(): ignored (disabled)')
            return
        logger.info("check_region_in_frame_by_selector('%s')" % tag)
        # We need to temporarily save the hide_scrollbars value, since we'll change it to make sure that hide_scrollbars
        # will NOT be called twice (once outside the frame and once inside the frame).
        original_hide_scrollbars_value = self.hide_scrollbars
        if self.hide_scrollbars:
            original_overflow = self._driver.hide_scrollbars()
            self.hide_scrollbars = False
        # Switching to the relevant frame
        self._driver.switch_to.frame(frame_reference)
        logger.debug("calling 'check_region_by_selector'...")
        self.check_region_by_selector(by, value, tag, match_timeout)
        # Switching back to our original frame
        self._driver.switch_to.parent_frame()
        if original_hide_scrollbars_value:
            # noinspection PyUnboundLocalVariable
            self._driver.set_overflow(original_overflow)
            self.hide_scrollbars = original_hide_scrollbars_value
예제 #4
0
 def check_region(self, region, tag=None, match_timeout=-1):
     """
     Takes a snapshot of the given region from the browser using the web driver and matches it
     with the expected output. If the current context is a frame, the region is offsetted
     relative to the frame.
     Args:
         :param region: (Region) The region which will be visually validated. The coordinates are
                          relative to the viewport of the current frame.
         :param tag: (str) Description of the visual validation checkpoint.
         :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
     Returns:
         None
     """
     if self.is_disabled:
         logger.info('check_region(): ignored (disabled)')
         return
     logger.info("check_region([%s], '%s')" % (region, tag))
     if region.is_empty():
         raise EyesError("region cannot be empty!")
     if self.hide_scrollbars:
         original_overflow = self._driver.hide_scrollbars()
     self._prepare_to_check()
     result = self._match_window_task.match_region(region, match_timeout, tag,
                                                   self.force_full_page_screenshot,
                                                   self._user_inputs,
                                                   self._should_match_once_on_timeout)
     if self.hide_scrollbars:
         # noinspection PyUnboundLocalVariable
         self._driver.set_overflow(original_overflow)
     self._handle_match_result(result, tag)
예제 #5
0
 def add_mouse_trigger_by_element(self, action, element):
     """
     Adds a mouse trigger.
     Args:
         action: (string) Mouse action (click, double click etc.)
         element: (WebElement) The element on which the action was performed.
     """
     if self.is_disabled:
         logger.debug("add_mouse_trigger: Ignoring %s (disabled)" % action)
         return
     # Triggers are activated on the last checked window.
     if self._last_screenshot is None:
         logger.debug("add_mouse_trigger: Ignoring %s (no screenshot)" % action)
         return
     if not EyesFrame.is_same_frame_chain(self._driver.get_frame_chain(),
                                          self._last_screenshot.get_frame_chain()):
         logger.debug("add_mouse_trigger: Ignoring %s (different frame)" % action)
         return
     control = self._last_screenshot.get_intersected_region_by_element(element)
     # Making sure the trigger is within the last screenshot bounds
     if control.is_empty():
         logger.debug("add_mouse_trigger: Ignoring %s (out of bounds)" % action)
         return
     cursor = control.middle_offset
     trigger = MouseTrigger(action, control, cursor)
     self._user_inputs.append(trigger)
     logger.info("add_mouse_trigger: Added %s" % trigger)
예제 #6
0
    def check_region(self, region, tag=None, match_timeout=-1):
        """
        Takes a snapshot of the given region from the browser using the web driver and matches it
        with the expected output. If the current context is a frame, the region is offsetted
        relative to the frame.

        :param region: (Region) The region which will be visually validated. The coordinates are
                         relative to the viewport of the current frame.
        :param tag: (str) Description of the visual validation checkpoint.
        :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        :return: None
        """
        if self.is_disabled:
            logger.info('check_region(): ignored (disabled)')
            return
        logger.info("check_region([%s], '%s')" % (region, tag))
        if region.is_empty():
            raise EyesError("region cannot be empty!")
        if self.hide_scrollbars:
            original_overflow = self._driver.hide_scrollbars()
        self._prepare_to_check()
        result = self._match_window_task.match_region(region, match_timeout, tag,
                                                      self.force_full_page_screenshot,
                                                      self._user_inputs,
                                                      self._should_match_once_on_timeout)
        if self.hide_scrollbars:
            # noinspection PyUnboundLocalVariable
            self._driver.set_overflow(original_overflow)
        self._handle_match_result(result, tag)
예제 #7
0
 def add_text_trigger_by_element(self, element, text):
     """
     Adds a text trigger.
     Args:
         element: (WebElement) The element to which the text was sent.
         text: (str) The trigger's text.
     """
     if self.is_disabled:
         logger.debug("add_text_trigger: Ignoring '%s' (disabled)" % text)
         return
     # Triggers are activated on the last checked window.
     if self._last_screenshot is None:
         logger.debug("add_text_trigger: Ignoring '%s' (no screenshot)" % text)
         return
     if not EyesFrame.is_same_frame_chain(self._driver.get_frame_chain(),
                                          self._last_screenshot.get_frame_chain()):
         logger.debug("add_text_trigger: Ignoring %s (different frame)" % text)
         return
     control = self._last_screenshot.get_intersected_region_by_element(element)
     # Making sure the trigger is within the last screenshot bounds
     if control.is_empty():
         logger.debug("add_text_trigger: Ignoring %s (out of bounds)" % text)
         return
     trigger = TextTrigger(control, text)
     self._user_inputs.append(trigger)
     logger.info("add_text_trigger: Added %s" % trigger)
예제 #8
0
    def add_mouse_trigger_by_element(self, action, element):
        """
        Adds a mouse trigger.

        :param action: Mouse action (click, double click etc.)
        :param element: The element on which the action was performed.
        """
        if self.is_disabled:
            logger.debug("add_mouse_trigger: Ignoring %s (disabled)" % action)
            return
        # Triggers are activated on the last checked window.
        if self._last_screenshot is None:
            logger.debug("add_mouse_trigger: Ignoring %s (no screenshot)" % action)
            return
        if not EyesFrame.is_same_frame_chain(self._driver.get_frame_chain(),
                                             self._last_screenshot.get_frame_chain()):
            logger.debug("add_mouse_trigger: Ignoring %s (different frame)" % action)
            return
        control = self._last_screenshot.get_intersected_region_by_element(element)
        # Making sure the trigger is within the last screenshot bounds
        if control.is_empty():
            logger.debug("add_mouse_trigger: Ignoring %s (out of bounds)" % action)
            return
        cursor = control.middle_offset
        trigger = MouseTrigger(action, control, cursor)
        self._user_inputs.append(trigger)
        logger.info("add_mouse_trigger: Added %s" % trigger)
예제 #9
0
 def check_window(self, tag=None, specific_match_timeout=-1):
     """
     Takes a snapshot from the browser using the web driver and matches it with the expected
     output.
     Args:
         (str) tag: (Optional) Description of the visual validation checkpoint.
         (float) specific_match_timeout: (Optional) Timeout for the visual validation
                                         checkpoint (given in order to let the current page time
                                         to stabilize).
     Returns:
         None
     """
     if self.is_disabled:
         logger.info("check_window(%s): ignored (disabled)" % tag)
         return
     logger.info("check_window('%s')" % tag)
     if self.hide_scrollbars:
         original_overflow = self._driver.hide_scrollbars()
     self._prepare_to_check()
     result = self._match_window_task.match_window(specific_match_timeout, tag,
                                                   self.force_full_page_screenshot,
                                                   self._user_inputs,
                                                   self._should_match_once_on_timeout)
     if self.hide_scrollbars:
         # noinspection PyUnboundLocalVariable
         self._driver.set_overflow(original_overflow)
     self._handle_match_result(result, tag)
예제 #10
0
    def check_region_by_element(self, element, tag=None, match_timeout=-1):
        """
        Takes a snapshot of the region of the given element from the browser using the web driver
        and matches it with the expected output.
        Args:
            :param element: (WebElement)  The element which region will be visually validated.
            :param tag: (str) Description of the visual validation checkpoint.
            :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        Returns:
            None
        """
        if self.is_disabled:
            logger.info('check_region_by_element(): ignored (disabled)')
            return
        logger.info("check_region_by_element('%s')" % tag)
        if self.hide_scrollbars:
            original_overflow = self._driver.hide_scrollbars()
        self._prepare_to_check()
        result = self._match_window_task.match_element(element, match_timeout, tag,
                                                       self.force_full_page_screenshot,
                                                       self._user_inputs,
                                                       self._should_match_once_on_timeout)

        if self.hide_scrollbars:
            # noinspection PyUnboundLocalVariable
            self._driver.set_overflow(original_overflow)
        self._handle_match_result(result, tag)
예제 #11
0
    def check_region_by_element(self, element, tag=None, match_timeout=-1):
        """
        Takes a snapshot of the region of the given element from the browser using the web driver
        and matches it with the expected output.

        :param element: (WebElement)  The element which region will be visually validated.
        :param tag: (str) Description of the visual validation checkpoint.
        :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        :return: None
        """
        if self.is_disabled:
            logger.info('check_region_by_element(): ignored (disabled)')
            return
        logger.info("check_region_by_element('%s')" % tag)
        if self.hide_scrollbars:
            original_overflow = self._driver.hide_scrollbars()
        self._prepare_to_check()
        result = self._match_window_task.match_element(element, match_timeout, tag,
                                                       self.force_full_page_screenshot,
                                                       self._user_inputs,
                                                       self._should_match_once_on_timeout)

        if self.hide_scrollbars:
            # noinspection PyUnboundLocalVariable
            self._driver.set_overflow(original_overflow)
        self._handle_match_result(result, tag)
예제 #12
0
 def _handle_match_result(self, result, tag):
     self._last_screenshot = result['screenshot']
     as_expected = result['as_expected']
     self._user_inputs = []
     if not as_expected:
         self._should_match_once_on_timeout = True
         if not self._running_session['is_new_session']:
             logger.info("Window mismatch %s" % tag)
             if self.failure_reports == FailureReports.IMMEDIATE:
                 raise TestFailedError("Mismatch found in '%s' of '%s'" %
                                       (self._start_info['scenarioIdOrName'],
                                        self._start_info['appIdOrName']))
예제 #13
0
 def _handle_match_result(self, result, tag):
     self._last_screenshot = result['screenshot']
     as_expected = result['as_expected']
     self._user_inputs = []
     if not as_expected:
         self._should_match_once_on_timeout = True
         if not self._running_session['is_new_session']:
             logger.info("Window mismatch %s" % tag)
             if self.failure_reports == FailureReports.IMMEDIATE:
                 raise TestFailedError("Mismatch found in '%s' of '%s'" %
                                       (self._start_info['scenarioIdOrName'],
                                        self._start_info['appIdOrName']))
def get_viewport_size(driver):
    """
    Tries to get the viewport size using Javascript. If fails, gets the entire browser window
    size!
    :param driver: The webdriver to use for getting the viewport size.
    """
    # noinspection PyBroadException
    try:
        width, height = driver.execute_script(_JS_GET_VIEWPORT_SIZE)
        return {'width': width, 'height': height}
    except:
        logger.info('Failed to get viewport size. Only window size is available')
        return driver.get_window_size()
예제 #15
0
    def _get_environment(self):
        """
        Application environment is the environment (e.g., the host OS) which runs the application under test.

        :return: The current application environment.
        """
        os = self.host_os
        # If no host OS was set, check for mobile OS.
        if os is None:
            logger.info('No OS set, checking for mobile OS...')
            # Since in Python Appium driver is the same for Android and iOS, we need to use the desired
            # capabilities to figure this out.
            if self._driver.is_mobile_device():
                platform_name = self._driver.get_platform_name()
                logger.info(platform_name + ' detected')
                platform_version = self._driver.get_platform_version()
                if platform_version is not None:
                    # Notice that Python's "split" function's +limit+ is the the maximum splits performed
                    # whereas in Ruby it is the maximum number of elements in the result (which is why they are set
                    # differently).
                    major_version = platform_version.split('.', 1)[0]
                    os = platform_name + ' ' + major_version
                else:
                    os = platform_name
                logger.info("Setting OS: " + os)
            else:
                logger.info('No mobile OS detected.')
        app_env = {'os': os, 'hostingApp': self.host_app,
                   'displaySize': self._viewport_size,
                   'inferred': self._get_inferred_environment()}
        return app_env
예제 #16
0
 def _get_environment(self):
     """
     Application environment is the environment (e.g., the host OS) which runs the application under test.
     :return: The current application environment.
     """
     os = self.host_os
     # If no host OS was set, check for mobile OS.
     if os is None:
         logger.info('No OS set, checking for mobile OS...')
         # Since in Python Appium driver is the same for Android and iOS, we need to use the desired
         # capabilities to figure this out.
         if self._driver.is_mobile_device():
             platform_name = self._driver.get_platform_name()
             logger.info(platform_name + ' detected')
             platform_version = self._driver.get_platform_version()
             if platform_version is not None:
                 # Notice that Python's "split" function's +limit+ is the the maximum splits performed
                 # whereas in Ruby it is the maximum number of elements in the result (which is why they are set
                 # differently).
                 major_version = platform_version.split('.', 1)[0]
                 os = platform_name + ' ' + major_version
             else:
                 os = platform_name
             logger.info("Setting OS: " + os)
         else:
             logger.info('No mobile OS detected.')
     app_env = {'os': os, 'hostingApp': self.host_app,
                'displaySize': self._viewport_size,
                'inferred': self._get_inferred_environment()}
     return app_env
예제 #17
0
def get_viewport_size(driver):
    """
    Tries to get the viewport size using Javascript. If fails, gets the entire browser window
    size!
    :param driver: The webdriver to use for getting the viewport size.
    """
    # noinspection PyBroadException
    try:
        width, height = driver.execute_script(_JS_GET_VIEWPORT_SIZE)
        return {'width': width, 'height': height}
    except:
        logger.info(
            'Failed to get viewport size. Only window size is available')
        return driver.get_window_size()
def get_viewport_size(driver):
    """
    Tries to get the viewport size using Javascript. If fails, gets the entire browser window
    size!
    """
    # noinspection PyBroadException
    try:
        width = extract_viewport_width(driver)
        height = extract_viewport_height(driver)
        return {'width': width, 'height': height}
    except:
        logger.info('Failed to get viewport size. Only window size is available')
        browser_size = driver.get_window_size()
        return {'width': browser_size['width'], 'height': browser_size['height']}
예제 #19
0
    def check_region_by_selector(self, by, value, tag=None, match_timeout=-1):
        """
        Takes a snapshot of the region of the element found by calling find_element(by, value)
        and matches it with the expected output.

        :param by: (By) The way by which an element to be validated should be found (e.g., By.ID).
        :param value: (str) The value identifying the element using the "by" type.
        :param tag: (str) Description of the visual validation checkpoint.
        :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        :return: None
        """
        if self.is_disabled:
            logger.info('check_region_by_selector(): ignored (disabled)')
            return
        logger.debug("calling 'check_region_by_element'...")
        self.check_region_by_element(self._driver.find_element(by, value), tag,
                                     match_timeout)
def set_viewport_size(driver, required_size):
    if 'width' not in required_size or 'height' not in required_size:
        raise EyesError('Size must have width & height keys!')

    logger.debug("set_viewport_size({0})".format(required_size))
    starting_size = required_size
    # When setting the viewport size we need to be in the default content frame
    original_frame_chain = driver.get_frame_chain()
    driver.switch_to.default_content()

    driver.set_window_size(required_size['width'], required_size['height'])
    if not _verify_size(driver.get_window_size, starting_size):
        error_message = "Failed to set browser size!"
        logger.info(error_message)
        # Going back to the frame we started at
        driver.switch_to.frames(original_frame_chain)
        raise TestFailedError(error_message)
    current_viewport_size = get_viewport_size(driver)
    logger.debug("set_viewport_size(): initial viewport size: {0}".format(current_viewport_size))
    current_browser_size = driver.get_window_size()
    width_to_set = (2 * current_browser_size['width']) - current_viewport_size['width']
    height_to_set = (2 * current_browser_size['height']) - current_viewport_size['height']
    driver.set_window_size(width_to_set, height_to_set)
    if not _verify_size(lambda: get_viewport_size(driver), required_size):
        current_viewport_size = get_viewport_size(driver)
        logger.debug("set_viewport_size(): viewport size: {0}".format(current_viewport_size))
        logger.debug("set_viewport_size(): attempting one more time...")
        current_browser_size = driver.get_window_size()
        updated_width = current_browser_size['width'] + (required_size['width']
                                                         - current_viewport_size['width'])
        updated_height = current_browser_size['height'] + (required_size['height']
                                                           - current_viewport_size['height'])
        updated_browser_size = {'width': updated_width, 'height': updated_height}
        logger.debug("set_viewport_size(): browser size: {0}".format(current_browser_size))
        logger.debug("set_viewport_size(): required browser size: {0}".format(updated_browser_size))
        driver.set_window_size(updated_width, updated_height)
        if not _verify_size(lambda: get_viewport_size(driver), required_size):
            current_viewport_size = get_viewport_size(driver)
            logger.debug("set_viewport_size(): viewport size: {0}".format(current_viewport_size))
            error_message = "Failed to set viewport size."
            logger.info(error_message)
            # Going back to the frame we started at
            driver.switch_to.frames(original_frame_chain)
            raise TestFailedError(error_message)
    # Going back to the frame we started at
    driver.switch_to.frames(original_frame_chain)
예제 #21
0
    def open(self, driver, app_name, test_name, viewport_size=None):
        """
        Starts a test.

        Args:
            params (dictionary):
                app_name (str): The name of the application under test.
                test_name (str): The test name.
                (Optional) viewport_size ({width, height}): The client's viewport size (i.e.,
                                                            the visible part of the document's
                                                            body) or None to allow any viewport
                                                            size.
        Returns:
            An updated web driver
        Raises:
            EyesError
        """
        logger.open_()
        if self.is_disabled:
            logger.debug('open(): ignored (disabled)')
            return driver

        if self.api_key is None:
            raise EyesError("API key not set! Log in to https://applitools.com to obtain your"
                            " API Key and use 'api_key' to set it.")

        if isinstance(driver, RemoteWebDriver):
            self._driver = EyesWebDriver(driver, self)
        elif isinstance(driver, EyesWebDriver):
            # If the driver is an EyesWebDriver (as might be the case when tests are ran
            # consecutively using the same driver object)
            self._driver = driver
        else:
            raise EyesError("driver is not a RemoteWebDriver ({0})".format(driver.__class__))

        logger.info("open(%s, %s, %s, %s)" % (app_name, test_name, viewport_size, self.failure_reports))

        if self.is_open():
            self.abort_if_not_closed()
            raise EyesError('a test is already running')
        self._app_name = app_name
        self._test_name = test_name
        self._viewport_size = viewport_size
        self._is_open = True
        return self._driver
예제 #22
0
 def check_region_by_selector(self, by, value, tag=None, match_timeout=-1):
     """
     Takes a snapshot of the region of the element found by calling find_element(by, value)
     and matches it with the expected output.
     Args:
         :param by: (By) The way by which an element to be validated should be found (e.g., By.ID).
         :param value: (str) The value identifying the element using the "by" type.
         :param tag: (str) Description of the visual validation checkpoint.
         :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
     Returns:
         None
     """
     if self.is_disabled:
         logger.info('check_region_by_selector(): ignored (disabled)')
         return
     logger.debug("calling 'check_region_by_element'...")
     self.check_region_by_element(self._driver.find_element(by, value), tag,
                                  match_timeout)
예제 #23
0
 def get_screenshot_as_base64(self):
     """
     Gets the screenshot of the current window as a base64 encoded string
        which is useful in embedded images in HTML.
     """
     screenshot64 = self.driver.get_screenshot_as_base64()
     display_rotation = self.get_display_rotation()
     if display_rotation != 0:
         logger.info('Rotation required.')
         num_quadrants = int(-(display_rotation / 90))
         logger.debug('decoding base64...')
         screenshot_bytes = base64.b64decode(screenshot64)
         logger.debug('Done! Creating image object...')
         screenshot = _image_utils.png_image_from_bytes(screenshot_bytes)
         logger.debug('Done! Rotating...')
         screenshot.quadrant_rotate(num_quadrants)
         screenshot64 = screenshot.get_base64()
     return screenshot64
예제 #24
0
 def check_region_by_selector(self, by, value, tag=None, specific_match_timeout=-1):
     """
     Takes a snapshot of the region of the element found by calling find_element(by, value)
     and matches it with the expected output.
     Args:
         (By) by: The way by which an element to be validated should be found (e.g., By.ID).
         (str) selector: The value identifying the element using the "by" type.
         (str) tag: (Optional) Description of the visual validation checkpoint.
         (float) specific_match_timeout: (Optional) Timeout for the visual validation
                                         checkpoint (given in order to let the current page time
                                         to stabilize).
     Returns:
         None
     """
     if self.is_disabled:
         logger.info('check_region_by_selector(): ignored (disabled)')
         return
     logger.debug("calling 'check_region_by_element'...")
     self.check_region_by_element(self._driver.find_element(by, value), tag,
                                  specific_match_timeout)
예제 #25
0
    def get_screenshot_as_base64(self):
        """
        Gets the screenshot of the current window as a base64 encoded string
           which is useful in embedded images in HTML.

        :Usage:
            driver.get_screenshot_as_base64()
        """
        screenshot64 = self.driver.get_screenshot_as_base64()
        display_rotation = self.get_display_rotation()
        if display_rotation != 0:
            logger.info('Rotation required.')
            num_quadrants = int(-(display_rotation / 90))
            logger.debug('decoding base64...')
            screenshot_bytes = base64.b64decode(screenshot64)
            logger.debug('Done! Creating image object...')
            screenshot = _image_utils.png_image_from_bytes(screenshot_bytes)
            logger.debug('Done! Rotating...')
            screenshot.quadrant_rotate(num_quadrants)
            screenshot64 = screenshot.get_base64()
        return screenshot64
예제 #26
0
    def open(self, driver, app_name, test_name, viewport_size=None):
        """
        Starts a test.

        :param driver: The webdriver to use.
        :param app_name: The name of the application under test.
        :param test_name: The test name.
        :param viewport_size: The client's viewport size (i.e., the visible part of the document's body) or None to
                                allow any viewport size.
        :return: An updated web driver
        :raise EyesError: If the session was already open.
        """
        logger.open_()
        if self.is_disabled:
            logger.debug('open(): ignored (disabled)')
            return driver

        if self.api_key is None:
            raise EyesError("API key not set! Log in to https://applitools.com to obtain your"
                            " API Key and use 'api_key' to set it.")

        if isinstance(driver, EyesWebDriver):
            # If the driver is an EyesWebDriver (as might be the case when tests are ran
            # consecutively using the same driver object)
            self._driver = driver
        else:
            if not isinstance(driver, RemoteWebDriver):
                logger.info("WARNING: driver is not a RemoteWebDriver (class: {0})".format(driver.__class__))
            self._driver = EyesWebDriver(driver, self, self._stitch_mode)

        logger.info("open(%s, %s, %s, %s)" % (app_name, test_name, viewport_size, self.failure_reports))

        if self.is_open():
            self.abort_if_not_closed()
            raise EyesError('a test is already running')
        self._app_name = app_name
        self._test_name = test_name
        self._viewport_size = viewport_size
        self._is_open = True
        return self._driver
예제 #27
0
    def abort_if_not_closed(self):
        """
        If a test is running, aborts it. Otherwise, does nothing.
        """
        if self.is_disabled:
            logger.debug('abort_if_not_closed(): ignored (disabled)')
            return
        try:
            self._reset_last_screenshot()

            if self._running_session:
                logger.debug('abort_if_not_closed(): Aborting session...')
                try:
                    self._agent_connector.stop_session(self._running_session, True, False)
                    logger.info('--- Test aborted.')
                except EyesError as e:
                    logger.info("Failed to abort server session: %s " % e)
                    pass
                finally:
                    self._running_session = None
        finally:
            logger.close()
예제 #28
0
    def abort_if_not_closed(self):
        """
        If a test is running, aborts it. Otherwise, does nothing.
        """
        if self.is_disabled:
            logger.debug('abort_if_not_closed(): ignored (disabled)')
            return
        try:
            self._reset_last_screenshot()

            if self._running_session:
                logger.debug('abort_if_not_closed(): Aborting session...')
                try:
                    self._agent_connector.stop_session(self._running_session, True, False)
                    logger.info('--- Test aborted.')
                except EyesError as e:
                    logger.info("Failed to abort server session: %s " % e)
                    pass
                finally:
                    self._running_session = None
        finally:
            logger.close()
예제 #29
0
def driver(request, browser_config):
    test_name = request.node.name
    build_tag = os.environ.get('BUILD_TAG', None)
    tunnel_id = os.environ.get('TUNNEL_IDENTIFIER', None)
    username = os.environ.get('SAUCE_USERNAME', None)
    access_key = os.environ.get('SAUCE_ACCESS_KEY', None)

    selenium_url = os.environ.get('SELENIUM_SERVER_URL',
                                  'http://127.0.0.1:4444/wd/hub')
    if 'ondemand.saucelabs.com' in selenium_url:
        selenium_url = "https://%s:%[email protected]:443/wd/hub" % (
            username, access_key)
    logger.debug('SELENIUM_URL={}'.format(selenium_url))

    desired_caps = browser_config.copy()
    desired_caps['build'] = build_tag
    desired_caps['tunnelIdentifier'] = tunnel_id
    desired_caps['name'] = test_name

    executor = RemoteConnection(selenium_url, resolve_ip=False)
    browser = webdriver.Remote(command_executor=executor,
                               desired_capabilities=desired_caps)
    if browser is None:
        raise WebDriverException("Never created!")

    yield browser

    # report results
    try:
        browser.execute_script("sauce:job-result=%s" %
                               str(not request.node.rep_call.failed).lower())
    except WebDriverException:
        # we can ignore the exceptions of WebDriverException type -> We're done with tests.
        logger.info(
            'Warning: The driver failed to quit properly. Check test and server side logs.'
        )
    finally:
        browser.quit()
예제 #30
0
    def get_full_page_screenshot(self):
        logger.info('getting full page screenshot..')

        # Saving the current frame reference and moving to the outermost frame.
        original_frame = self.get_frame_chain()
        original_scroll_position = self.get_current_scroll_position()
        self.switch_to.default_content()

        self.scroll_to(Point(0, 0))
        current_scroll_position = self.get_current_scroll_position()
        if current_scroll_position.x != 0 or current_scroll_position.y != 0:
            raise EyesError('Couldn\'t scroll to the top/left part of the screen!')

        entire_page_size = self.get_entire_page_size()

        # Starting with the screenshot at 0,0
        part64 = self.get_screenshot_as_base64()
        screenshot = _image_utils.png_image_from_bytes(base64.b64decode(part64))

        # IMPORTANT This is required! Since when calculating the screenshot parts for full size,
        # we use a screenshot size which is a bit smaller (see comment below).
        if (screenshot.width >= entire_page_size['width']) and \
                (screenshot.height >= entire_page_size['height']):
            self.switch_to.frames(original_frame)
            self.scroll_to(original_scroll_position)
            return screenshot

        #  We use a smaller size than the actual screenshot size in order to eliminate duplication
        #  of bottom scroll bars, as well as footer-like elements with fixed position.
        screenshot_part_size = {'width': screenshot.width,
                                'height': max(screenshot.height - self._MAX_SCROLL_BAR_SIZE,
                                              self._MIN_SCREENSHOT_PART_HEIGHT)}

        logger.debug("Total size: {0}, Screenshot part size: {1}".format(entire_page_size,
                                                                         screenshot_part_size))

        entire_page = Region(0, 0, entire_page_size['width'], entire_page_size['height'])
        screenshot_parts = entire_page.get_sub_regions(screenshot_part_size)

        # Starting with the screenshot we already captured at (0,0).
        stitched_image = screenshot

        for part in screenshot_parts:
            # Since we already took the screenshot for 0,0
            if part.left == 0 and part.top == 0:
                logger.debug('Skipping screenshot for 0,0 (already taken)')
                continue
            logger.debug("Taking screenshot for {0}".format(part))
            # Scroll to the part's top/left and give it time to stabilize.
            self.scroll_to(Point(part.left, part.top))
            time.sleep(0.1)
            # Since screen size might cause the scroll to reach only part of the way
            current_scroll_position = self.get_current_scroll_position()
            logger.debug("Scrolled To ({0},{1})".format(current_scroll_position.x,
                                                        current_scroll_position.y))
            part64 = self.get_screenshot_as_base64()
            part_image = _image_utils.png_image_from_bytes(base64.b64decode(part64))
            stitched_image.paste(current_scroll_position.x, current_scroll_position.y,
                                 part_image.pixel_bytes)

        self.switch_to.frames(original_frame)
        self.scroll_to(original_scroll_position)

        return stitched_image
예제 #31
0
    def get_full_page_screenshot(self):
        logger.info('getting full page screenshot..')

        # Saving the current frame reference and moving to the outermost frame.
        original_frame = self.get_frame_chain()
        self.switch_to.default_content()

        self.reset_origin()

        entire_page_size = self.get_entire_page_size()

        # Starting with the screenshot at 0,0
        part64 = self.get_screenshot_as_base64()
        screenshot = _image_utils.png_image_from_bytes(
            base64.b64decode(part64))

        # IMPORTANT This is required! Since when calculating the screenshot parts for full size,
        # we use a screenshot size which is a bit smaller (see comment below).
        if (screenshot.width >= entire_page_size['width']) and \
                (screenshot.height >= entire_page_size['height']):
            self.restore_origin()
            self.switch_to.frames(original_frame)
            return screenshot

        #  We use a smaller size than the actual screenshot size in order to eliminate duplication
        #  of bottom scroll bars, as well as footer-like elements with fixed position.
        screenshot_part_size = {
            'width':
            screenshot.width,
            'height':
            max(screenshot.height - self._MAX_SCROLL_BAR_SIZE,
                self._MIN_SCREENSHOT_PART_HEIGHT)
        }

        logger.debug("Total size: {0}, Screenshot part size: {1}".format(
            entire_page_size, screenshot_part_size))

        entire_page = Region(0, 0, entire_page_size['width'],
                             entire_page_size['height'])
        screenshot_parts = entire_page.get_sub_regions(screenshot_part_size)

        # Starting with the screenshot we already captured at (0,0).
        stitched_image = screenshot

        self.save_position()

        for part in screenshot_parts:
            # Since we already took the screenshot for 0,0
            if part.left == 0 and part.top == 0:
                logger.debug('Skipping screenshot for 0,0 (already taken)')
                continue
            logger.debug("Taking screenshot for {0}".format(part))
            # Scroll to the part's top/left and give it time to stabilize.
            self.scroll_to(Point(part.left, part.top))
            time.sleep(0.1)
            # Since screen size might cause the scroll to reach only part of the way
            current_scroll_position = self.get_current_position()
            logger.debug("Scrolled To ({0},{1})".format(
                current_scroll_position.x, current_scroll_position.y))
            part64 = self.get_screenshot_as_base64()
            part_image = _image_utils.png_image_from_bytes(
                base64.b64decode(part64))
            stitched_image.paste(current_scroll_position.x,
                                 current_scroll_position.y,
                                 part_image.pixel_bytes)

        self.restore_position()
        self.restore_origin()
        self.switch_to.frames(original_frame)

        return stitched_image
예제 #32
0
    def close(self, raise_ex=True):
        """
        Ends the test.
        Args:
            (boolean) raise_ex: If true, an exception will be raised for failed/new tests.
        Returns:
            The test results.
        """
        if self.is_disabled:
            logger.debug('close(): ignored (disabled)')
            return
        try:
            self._is_open = False

            logger.debug('close()')

            self._reset_last_screenshot()

            # If there's no running session, we simply return the default test results.
            if not self._running_session:
                logger.debug('close(): Server session was not started')
                logger.info('close(): --- Empty test ended.')
                return TestResults()

            results_url = self._running_session['session_url']
            is_new_session = self._running_session['is_new_session']
            should_save = (is_new_session and self.save_new_tests) or \
                          ((not is_new_session) and self.save_failed_tests)
            logger.debug("close(): automatically save session? %s" % should_save)
            logger.info('close(): Closing session...')
            results = self._agent_connector.stop_session(self._running_session, False, should_save)
            results.is_new = is_new_session
            results.url = results_url
            logger.info("close(): %s" % results)
            self._running_session = None

            if not is_new_session and (0 < results.mismatches or 0 < results.missing):
                # Test failed
                logger.info("--- Failed test ended. See details at " + results_url)
                if raise_ex:
                    message = "'%s' of '%s'. See details at %s" % (self._start_info['scenarioIdOrName'],
                                                                   self._start_info['appIdOrName'],
                                                                   results_url)
                    raise TestFailedError(message, results)
                return results
            if is_new_session:
                # New test
                instructions = "Please approve the new baseline at %s" % results_url
                logger.info("--- New test ended. %s" % instructions)
                if raise_ex and self.fail_on_new_test:
                    message = "'%s' of '%s'. %s" % (self._start_info['scenarioIdOrName'],
                                                    self._start_info['appIdOrName'], instructions)
                    raise NewTestError(message, results)
                return results
            # Test passed
            logger.info('--- Test passed.')
            return results
        finally:
            logger.close()
예제 #33
0
    def close(self, raise_ex=True):
        """
        Ends the test.

        :param raise_ex: If true, an exception will be raised for failed/new tests.
        :return: The test results.
        """
        if self.is_disabled:
            logger.debug('close(): ignored (disabled)')
            return
        try:
            self._is_open = False

            logger.debug('close()')

            self._reset_last_screenshot()

            # If there's no running session, we simply return the default test results.
            if not self._running_session:
                logger.debug('close(): Server session was not started')
                logger.info('close(): --- Empty test ended.')
                return TestResults()

            results_url = self._running_session['session_url']
            is_new_session = self._running_session['is_new_session']
            should_save = (is_new_session and self.save_new_tests) or \
                          ((not is_new_session) and self.save_failed_tests)
            logger.debug("close(): automatically save session? %s" % should_save)
            logger.info('close(): Closing session...')
            results = self._agent_connector.stop_session(self._running_session, False, should_save)
            results.is_new = is_new_session
            results.url = results_url
            logger.info("close(): %s" % results)
            self._running_session = None

            if not is_new_session and (0 < results.mismatches or 0 < results.missing):
                # Test failed
                logger.info("--- Failed test ended. See details at " + results_url)
                if raise_ex:
                    message = "'%s' of '%s'. See details at %s" % (self._start_info['scenarioIdOrName'],
                                                                   self._start_info['appIdOrName'],
                                                                   results_url)
                    raise TestFailedError(message, results)
                return results
            if is_new_session:
                # New test
                instructions = "Please approve the new baseline at %s" % results_url
                logger.info("--- New test ended. %s" % instructions)
                if raise_ex and self.fail_on_new_test:
                    message = "'%s' of '%s'. %s" % (self._start_info['scenarioIdOrName'],
                                                    self._start_info['appIdOrName'], instructions)
                    raise NewTestError(message, results)
                return results
            # Test passed
            logger.info('--- Test passed.')
            return results
        finally:
            logger.close()