Exemple #1
0
 def _viewport_screenshot(self, scale_provider):
     # type: (ScaleProvider) -> EyesWebDriverScreenshot
     logger.info('Viewport screenshot requested')
     screenshot64 = self._driver.get_screesnhot_as_base64_from_main_frame(
         self.seconds_to_wait_screenshot)
     screenshot = image_utils.image_from_bytes(
         base64.b64decode(screenshot64))
     scale_provider.update_scale_ratio(screenshot.width)
     pixel_ratio = 1 / scale_provider.scale_ratio
     if pixel_ratio != 1.0:
         screenshot = image_utils.scale_image(screenshot, 1.0 / pixel_ratio)
     return EyesWebDriverScreenshot.create_from_image(
         screenshot, self._driver).get_viewport_screenshot()
Exemple #2
0
    def _viewport_screenshot(self, scale_provider):
        # type: (ScaleProvider) -> EyesWebDriverScreenshot
        logger.info("Viewport screenshot requested")

        self._driver._wait_before_screenshot(self._seconds_to_wait_screenshot)
        if not self._driver.is_mobile_device():
            image64 = self._driver.get_screesnhot_as_base64_from_main_frame()
        else:
            image64 = self._driver.get_screenshot_as_base64()

        image = image_utils.image_from_bytes(base64.b64decode(image64))
        scale_provider.update_scale_ratio(image.width)
        pixel_ratio = 1 / scale_provider.scale_ratio
        if pixel_ratio != 1.0:
            image = image_utils.scale_image(image, 1.0 / pixel_ratio)
        return EyesWebDriverScreenshot.create_from_image(
            image, self._driver).get_viewport_screenshot()
    def __init__(self,
                 driver,
                 screenshot=None,
                 screenshot64=None,
                 is_viewport_screenshot=None,
                 frame_location_in_screenshot=None):
        # type: (EyesWebDriver, Image.Image, None, tp.Optional[bool], tp.Optional[Point]) -> None
        """
        Initializes a Screenshot instance. Either screenshot or screenshot64 must NOT be None.
        Should not be used directly. Use create_from_image/create_from_base64 instead.

        :param driver: EyesWebDriver instance which handles the session from which the screenshot
                    was retrieved.
        :param screenshot: image instance. If screenshot64 is None,
                                    this variable must NOT be none.
        :param screenshot64: The base64 representation of a png image. If screenshot
                                     is None, this variable must NOT be none.
        :param is_viewport_screenshot: Whether the screenshot object represents a
                                                viewport screenshot or a full screenshot.
        :param frame_location_in_screenshot: The location of the frame relative
                                                    to the top,left of the screenshot.
        :raise EyesError: If the screenshots are None.
        """
        if screenshot is None and screenshot64 is None:
            raise EyesError("both screenshot and screenshot64 are None!")

        if screenshot64:
            screenshot = image_utils.image_from_bytes(
                base64.b64decode(screenshot64))

        # initializing of screenshot
        super(EyesWebDriverScreenshot, self).__init__(image=screenshot)

        self._driver = driver
        self._viewport_size = driver.get_default_content_viewport_size(
        )  # type: ViewPort

        self._frame_chain = driver.get_frame_chain()
        if self._frame_chain:
            chain_len = len(self._frame_chain)
            self._frame_size = self._frame_chain[chain_len - 1].size
        else:
            try:
                self._frame_size = driver.get_entire_page_size()
            except WebDriverException:
                # For Appium, we can't get the "entire page size", so we use the viewport size.
                self._frame_size = self._viewport_size
        # For native Appium Apps we can't get the scroll position, so we use (0,0)
        try:
            self._scroll_position = driver.get_current_position()
        except (WebDriverException, EyesError):
            self._scroll_position = Point(0, 0)
        if is_viewport_screenshot is None:
            is_viewport_screenshot = (
                self._screenshot.width <= self._viewport_size['width']
                and self._screenshot.height <= self._viewport_size['height'])
        self._is_viewport_screenshot = is_viewport_screenshot
        if frame_location_in_screenshot is None:
            if self._frame_chain:
                frame_location_in_screenshot = EyesWebDriverScreenshot \
                    .calc_frame_location_in_screenshot(self._frame_chain, is_viewport_screenshot)
            else:
                # The frame is the default content
                frame_location_in_screenshot = Point(0, 0)
                if self._is_viewport_screenshot:
                    frame_location_in_screenshot.offset(
                        -self._scroll_position.x, -self._scroll_position.y)
        self._frame_location_in_screenshot = frame_location_in_screenshot
        self._frame_screenshot_intersect = Region(
            frame_location_in_screenshot.x, frame_location_in_screenshot.y,
            self._frame_size['width'], self._frame_size['height'])
        self._frame_screenshot_intersect.intersect(
            Region(width=self._screenshot.width,
                   height=self._screenshot.height))
Exemple #4
0
    def get_stitched_screenshot(self, element, wait_before_screenshots, scale_provider):
        # type: (AnyWebElement, int, ScaleProvider) -> Image.Image
        """
        Gets a stitched screenshot for specific element

        :param wait_before_screenshots: Seconds to wait before taking each screenshot.
        :return: The full page screenshot.
        """
        logger.info('getting stitched element screenshot..')

        self._position_provider = ElementPositionProvider(self.driver, element)
        entire_size = self._position_provider.get_entire_size()

        #  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.
        pl = element.location

        # TODO: add correct values for Safari
        # in the safari browser the returned size has absolute value but not relative as
        # in other browsers

        origin_overflow = element.get_overflow()
        element.set_overflow('hidden')

        element_width = element.get_client_width()
        element_height = element.get_client_height()

        border_left_width = element.get_computed_style_int('border-left-width')
        border_top_width = element.get_computed_style_int('border-top-width')
        element_region = Region(pl['x'] + border_left_width,
                                pl['y'] + border_top_width,
                                element_width, element_height)
        logger.debug("Element region: {}".format(element_region))
        # Firefox 60 and above make a screenshot of the current frame when other browsers
        # make a screenshot of the viewport. So we scroll down to frame at _will_switch_to method
        # and add a left margin here.
        # TODO: Refactor code. Use EyesScreenshot
        if self._frames:
            if ((self.browser_name == 'firefox' and self.browser_version < 60.0)
                    or self.browser_name in ('chrome', 'MicrosoftEdge', 'internet explorer', 'safari')):
                element_region.left += int(self._frames[-1].location['x'])

        screenshot_part_size = {'width': element_region.width,
                                'height': max(element_region.height - self._MAX_SCROLL_BAR_SIZE,
                                              self._MIN_SCREENSHOT_PART_HEIGHT)}
        entire_element = Region(0, 0, entire_size['width'], entire_size['height'])

        screenshot_parts = entire_element.get_sub_regions(screenshot_part_size)
        viewport = self.get_viewport_size()
        screenshot = image_utils.image_from_bytes(base64.b64decode(self.get_screenshot_as_base64()))
        scale_provider.update_scale_ratio(screenshot.width)
        pixel_ratio = 1 / scale_provider.scale_ratio
        need_to_scale = True if pixel_ratio != 1.0 else False

        if need_to_scale:
            element_region = element_region.scale(scale_provider.device_pixel_ratio)

        # Starting with element region size part of the screenshot. Use it as a size template.
        stitched_image = Image.new('RGBA', (entire_element.width, entire_element.height))
        for part in screenshot_parts:
            logger.debug("Taking screenshot for {0}".format(part))
            # Scroll to the part's top/left and give it time to stabilize.
            self._position_provider.set_position(Point(part.left, part.top))
            EyesWebDriver._wait_before_screenshot(wait_before_screenshots)
            # Since screen size might cause the scroll to reach only part of the way
            current_scroll_position = self._position_provider.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.image_from_bytes(base64.b64decode(part64))
            # Cut to viewport size the full page screenshot of main frame for some browsers
            if self._frames:
                if (self.browser_name == 'firefox' and self.browser_version < 60.0
                        or self.browser_name in ('internet explorer', 'safari')):
                    # TODO: Refactor this to make main screenshot only once
                    frame_scroll_position = int(self._frames[-1].location['y'])
                    part_image = image_utils.get_image_part(part_image, Region(top=frame_scroll_position,
                                                                               height=viewport['height'],
                                                                               width=viewport['width']))
            # We cut original image before scaling to prevent appearing of artifacts
            part_image = image_utils.get_image_part(part_image, element_region)
            if need_to_scale:
                part_image = image_utils.scale_image(part_image, 1.0 / pixel_ratio)

            # first iteration
            if stitched_image is None:
                stitched_image = part_image
                continue
            stitched_image.paste(part_image, box=(current_scroll_position.x, current_scroll_position.y))

        if origin_overflow:
            element.set_overflow(origin_overflow)

        return stitched_image
Exemple #5
0
    def get_full_page_screenshot(self, wait_before_screenshots, scale_provider):
        # type: (Num, ScaleProvider) -> Image.Image
        """
        Gets a full page screenshot.

        :param wait_before_screenshots: Seconds to wait before taking each screenshot.
        :return: The full page screenshot.
        """
        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
        EyesWebDriver._wait_before_screenshot(wait_before_screenshots)
        part64 = self.get_screenshot_as_base64()
        screenshot = image_utils.image_from_bytes(base64.b64decode(part64))

        scale_provider.update_scale_ratio(screenshot.width)
        pixel_ratio = 1.0 / scale_provider.scale_ratio
        need_to_scale = True if pixel_ratio != 1.0 else False
        if need_to_scale:
            screenshot = image_utils.scale_image(screenshot, 1.0 / pixel_ratio)

        # 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 = Image.new('RGBA', (entire_page.width, entire_page.height))
        stitched_image.paste(screenshot, box=(0, 0))
        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))
            EyesWebDriver._wait_before_screenshot(wait_before_screenshots)
            # 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.image_from_bytes(base64.b64decode(part64))

            if need_to_scale:
                part_image = image_utils.scale_image(part_image, 1.0 / pixel_ratio)

            stitched_image.paste(part_image, box=(current_scroll_position.x, current_scroll_position.y))

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

        return stitched_image
    def get_stitched_screenshot(self, element_region, wait_before_screenshots,
                                scale_provider):
        # type: (Region, int, ScaleProvider) -> Image.Image
        """
        Gets a stitched screenshot for specific element

        :param element_region: Region of required screenshot
        :param wait_before_screenshots: Seconds to wait before taking each screenshot.
        :param scale_provider: Scale image if needed.
        :return: The full element screenshot.
        """
        logger.info("getting stitched element screenshot..")
        self._position_provider = self._eyes._element_position_provider
        entire_size = self._position_provider.get_entire_size()
        logger.debug("Element region: {}".format(element_region))
        # Firefox 60 and above make a screenshot of the current frame when other browsers
        # make a screenshot of the viewport. So we scroll down to frame at
        # _will_switch_to method and add a left margin here.
        # TODO: Refactor code. Use EyesScreenshot
        if self._frame_chain:
            if (self.browser_name == "firefox"
                    and self.browser_version < 60.0) or self.browser_name in (
                        "chrome",
                        "MicrosoftEdge",
                        "internet explorer",
                        "safari",
                    ):
                element_region.left += int(self._frame_chain.peek.location.x)

        screenshot_part_size = {
            "width":
            element_region.width,
            "height":
            max(
                element_region.height - self._MAX_SCROLL_BAR_SIZE,
                self._MIN_SCREENSHOT_PART_HEIGHT,
            ),
        }
        entire_element = Region(0, 0, entire_size["width"],
                                entire_size["height"])

        screenshot_parts = entire_element.get_sub_regions(screenshot_part_size)
        viewport = self.get_viewport_size()
        screenshot = image_utils.image_from_bytes(
            base64.b64decode(self.get_screenshot_as_base64()))
        scale_provider.update_scale_ratio(screenshot.width)
        pixel_ratio = 1 / scale_provider.scale_ratio
        need_to_scale = True if pixel_ratio != 1.0 else False

        if need_to_scale:
            element_region = element_region.scale(
                scale_provider.device_pixel_ratio)

        # Starting with element region size part of the screenshot. Use it as a size template.
        stitched_image = Image.new(
            "RGBA", (entire_element.width, entire_element.height))
        for part in screenshot_parts:
            logger.debug("Taking screenshot for {0}".format(part))
            # Scroll to the part's top/left and give it time to stabilize.
            self._position_provider.set_position(Point(part.left, part.top))
            EyesWebDriver._wait_before_screenshot(wait_before_screenshots)
            # Since screen size might cause the scroll to reach only part of the way
            current_scroll_position = self._position_provider.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.image_from_bytes(base64.b64decode(part64))
            # Cut to viewport size the full page screenshot of main frame for some browsers
            if self._frame_chain:
                if (self.browser_name == "firefox"
                        and self.browser_version < 60.0 or self.browser_name
                        in ("internet explorer", "safari")):
                    # TODO: Refactor this to make main screenshot only once
                    frame_scroll_position = int(
                        self._frame_chain.peek.location.y)
                    part_image = image_utils.get_image_part(
                        part_image,
                        Region(
                            top=frame_scroll_position,
                            height=viewport["height"],
                            width=viewport["width"],
                        ),
                    )
            # We cut original image before scaling to prevent appearing of artifacts
            part_image = image_utils.get_image_part(part_image, element_region)
            if need_to_scale:
                part_image = image_utils.scale_image(part_image,
                                                     1.0 / pixel_ratio)

            # first iteration
            if stitched_image is None:
                stitched_image = part_image
                continue
            stitched_image.paste(part_image,
                                 box=(current_scroll_position.x,
                                      current_scroll_position.y))

        self._position_provider = self._origin_position_provider
        return stitched_image