Exemple #1
0
 def check_image(self, image, tag=None, ignore_mismatch=False):
     # type: (Union[Image.Image, Text], Optional[Text], bool) -> Optional[bool]
     if self.is_disabled:
         return None
     logger.info("check_image(Image {}, tag {}, ignore_mismatch {}".format(
         image, tag, ignore_mismatch))
     return self._check_image(tag, ignore_mismatch, Target().image(image))
Exemple #2
0
 def _full_page_screenshot(self, scale_provider):
     # type: (ScaleProvider) -> EyesWebDriverScreenshot
     logger.info("Full page screenshot requested")
     screenshot = self._driver.get_full_page_screenshot(
         self._seconds_to_wait_screenshot, scale_provider)
     return EyesWebDriverScreenshot.create_from_image(
         screenshot, self._driver)
Exemple #3
0
    def check_region_by_element(self,
                                element,
                                tag=None,
                                match_timeout=-1,
                                target=None,
                                stitch_content=False):
        # type: (AnyWebElement, tp.Optional[str], int, tp.Optional[Target], bool) -> None
        """
        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).
        :param target: (Target) The target for the check_window call
        :return: None
        """
        logger.info("check_region_by_element('%s')" % tag)
        self._screenshot_type = self._obtain_screenshot_type(
            is_element=True,
            inside_a_frame=bool(self._driver.get_frame_chain()),
            stitch_content=stitch_content,
            force_fullpage=self.force_full_page_screenshot)
        self._region_to_check = element
        self._check_window_base(tag, match_timeout, target)
Exemple #4
0
    def check_region_by_element(self, element, tag=None, match_timeout=-1, target=None, stitch_content=False):
        # type: (AnyWebElement, tp.Optional[tp.Text], int, tp.Optional[Target], bool) -> None
        """
        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).
        :param target: (Target) The target for the check_window call
        :return: None
        """
        logger.info("check_region_by_element('%s')" % tag)
        self._screenshot_type = self._obtain_screenshot_type(is_element=True,
                                                             inside_a_frame=bool(self._driver.frame_chain),
                                                             stitch_content=stitch_content,
                                                             force_fullpage=self.force_full_page_screenshot)

        self._element_position_provider = ElementPositionProvider(self._driver, element)

        origin_overflow = element.get_overflow()
        eyes_selenium_utils.add_data_overflow_to_element(self.driver, element, origin_overflow)
        element.set_overflow('hidden')

        element_region = self._get_element_region(element)
        self._region_to_check = element_region
        self._check_window_base(tag, match_timeout, target)
        self._element_position_provider = None

        if origin_overflow:
            element.set_overflow(origin_overflow)
Exemple #5
0
    def check_region_in_frame_by_selector(self, frame_reference,  # type: FrameReference
                                          by,  # type: tp.Text
                                          value,  # type: tp.Text
                                          tag=None,  # type: tp.Optional[tp.Text]
                                          match_timeout=-1,  # type: int
                                          target=None,  # type: tp.Optional[Target]
                                          stitch_content=False  # type: bool
                                          ):
        # type: (...) -> None
        """
        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).
        :param target: (Target) The target for the check_window call
        :return: None
        """
        # TODO: remove this disable
        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)

        # Switching to the relevant frame
        with self._driver.switch_to.frame_and_back(frame_reference):
            logger.debug("calling 'check_region_by_selector'...")
            self.check_region_by_selector(by, value, tag, match_timeout, target, stitch_content)
Exemple #6
0
    def check_region(self,
                     region,
                     tag=None,
                     match_timeout=-1,
                     target=None,
                     stitch_content=False):
        # type: (Region, Optional[Text], int, Optional[Target], bool) -> None
        """
        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: The region which will be visually validated. The coordinates are
                       relative to the viewport of the current frame.
        :param tag: Description of the visual validation checkpoint.
        :param match_timeout: Timeout for the visual validation checkpoint
                              (milliseconds).
        :param target: The target for the check_window call
        :return: None
        """
        logger.info("check_region([%s], '%s')" % (region, tag))
        if region.is_empty:
            raise EyesError("region cannot be empty!")
        if target is None:
            target = Target()

        self._screenshot_type = self._obtain_screenshot_type(
            is_element=False,
            inside_a_frame=bool(self._driver.frame_chain),
            stitch_content=stitch_content,
            force_fullpage=self.force_full_page_screenshot,
            is_region=True,
        )
        self._region_to_check = region
        self._check_window_base(tag, match_timeout, target)
Exemple #7
0
    def open(self, driver, app_name, test_name, viewport_size=None):
        # type: (AnyWebDriver, Text, Text, Optional[ViewPort]) -> EyesWebDriver
        if self.is_disabled:
            logger.debug("open(): ignored (disabled)")
            return driver

        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)

        if viewport_size is not None:
            self._viewport_size = viewport_size
            eyes_selenium_utils.set_viewport_size(self._driver, viewport_size)

        self._ensure_viewport_size()
        self._open_base(app_name, test_name, viewport_size)

        return self._driver
Exemple #8
0
 def _region_or_screenshot(self, scale_provider):
     # type: (ScaleProvider) -> EyesWebDriverScreenshot
     logger.info('Not entire element screenshot requested')
     screenshot = self._viewport_screenshot(scale_provider)
     region = screenshot.get_element_region_in_frame_viewport(self._region_to_check)
     screenshot = screenshot.get_sub_screenshot_by_region(region)
     return screenshot
Exemple #9
0
 def _entire_element_screenshot(self, scale_provider):
     # type: (ScaleProvider) -> EyesWebDriverScreenshot
     logger.info('Entire element screenshot requested')
     screenshot = self._driver.get_stitched_screenshot(self._region_to_check,
                                                       self._seconds_to_wait_screenshot,
                                                       scale_provider)
     return EyesWebDriverScreenshot.create_from_image(screenshot, self._driver)
 def get_entire_size(self):
     try:
         size = {'width': self._element.get_scroll_width(), 'height': self._element.get_scroll_height()}
     except WebDriverException:
         raise EyesError('Failed to extract entire size!')
     logger.info("ElementPositionProvider - Entire size: {}".format(size))
     return size
Exemple #11
0
    def add_text_trigger_by_element(self, element, text):
        # type: (AnyWebElement, Text) -> None
        """
        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 self._driver.frame_chain == self._last_screenshot.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)
Exemple #12
0
    def add_mouse_trigger_by_element(self, action, element):
        # type: (Text, AnyWebElement) -> None
        """
        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 self._driver.frame_chain == self._last_screenshot.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)
Exemple #13
0
 def user_agent(self):
     try:
         user_agent = self.driver.execute_script("return navigator.userAgent")
         logger.info("user agent: {}".format(user_agent))
     except Exception as e:
         logger.info("Failed to obtain user-agent string")
         user_agent = None
     return user_agent
Exemple #14
0
 def check_region(self, image, region, tag=None, ignore_mismatch=False):
     # type: (Image.Image, Region, Optional[Text], bool) -> Optional[bool]
     if self.is_disabled:
         return None
     logger.info(
         "check_region(Image {}, region {}, tag {}, ignore_mismatch {}".
         format(image, region, tag, ignore_mismatch))
     return self._check_image(tag, ignore_mismatch,
                              Target().region(image, region))
def _download_css(base_url, value):
    # type: (str, str) -> str
    logger.info("Given URL to download: {}".format(value))
    if (general_utils.is_absolute_url(value) and
            not general_utils.is_url_with_scheme(value)):
        url = urljoin('http://', value)
    else:
        url = urljoin(base_url, value)
    logger.info("Download CSS from {}".format(url))
    return requests.get(url).text
Exemple #16
0
 def _region_or_screenshot(self, scale_provider):
     # type: (ScaleProvider) -> EyesWebDriverScreenshot
     logger.info('Not entire element screenshot requested')
     screenshot = self._viewport_screenshot(scale_provider)
     if isinstance(self._region_to_check, Region):
         screenshot = screenshot.get_sub_screenshot_by_region(
             self._region_to_check)
     else:
         screenshot = screenshot.get_sub_screenshot_by_element(
             self._region_to_check)
     return screenshot
Exemple #17
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 #18
0
 def _get_environment(self):
     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 eyes_selenium_utils.is_mobile_device(self._driver):
             platform_name = self._driver.platform_name
             logger.info(platform_name + ' detected')
             platform_version = self._driver.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
Exemple #19
0
 def _environment(self):
     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 eyes_selenium_utils.is_mobile_device(self._driver):
             platform_name = self._driver.platform_name
             logger.info(platform_name + " detected")
             platform_version = self._driver.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._inferred_environment,
     }
     return app_env
Exemple #20
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 get_viewport_size(driver):
    # type: (AnyWebDriver) -> ViewPort
    """
    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 dict(width=width, height=height)
    except WebDriverException:
        logger.info(
            "Failed to get viewport size. Only window size is available")
        return get_window_size(driver)
Exemple #22
0
    def check_window(self, tag=None, match_timeout=-1, target=None):
        # type: (tp.Optional[tp.Text], int, tp.Optional[Target]) -> None
        """
        Takes a snapshot from the browser using the web driver and matches it with the expected
        output.

        :param tag: (str) Description of the visual validation checkpoint.
        :param match_timeout: (int) Timeout for the visual validation checkpoint (milliseconds).
        :param target: (Target) The target for the check_window call
        :return: None
        """
        logger.info("check_window('%s')" % tag)
        self._screenshot_type = self._obtain_screenshot_type(is_element=False,
                                                             inside_a_frame=bool(self._driver.frame_chain),
                                                             stitch_content=False,
                                                             force_fullpage=self.force_full_page_screenshot)
        self._check_window_base(tag, match_timeout, target)
Exemple #23
0
def _get_frame_bundled_css(driver):
    # type: (EyesWebDriver) -> tp.Text
    base_url = driver.current_url  # type: ignore
    if not general_utils.is_absolute_url(base_url):
        logger.info("Base URL is not an absolute URL!")

    cssom_results = driver.execute_script(_CAPTURE_CSSOM_SCRIPT)
    raw_css_nodes = [
        CssNode.create(base_url, css_href, css_text)
        for css_text, css_href in cssom_results
    ]

    if len(raw_css_nodes) > 5:
        pool = mp.Pool(processes=mp.cpu_count() * 2)
        results = pool.map(_process_raw_css_node, raw_css_nodes)
    else:
        results = [_process_raw_css_node(node) for node in raw_css_nodes]
    return "".join(results)
Exemple #24
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)

    force_remote = request.config.getoption("remote", default=False)
    selenium_url = os.environ.get("SELENIUM_SERVER_URL", "http://127.0.0.1:4444/wd/hub")
    if "ondemand.saucelabs.com" in selenium_url or force_remote:
        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()
def _serialize_css(base_url, stylesheet):
    # type: (str, tp.List) -> str
    css_string = ''
    for node in stylesheet:
        add_as_is = True
        if node.type == 'at-rule' and node.lower_at_keyword == 'import':
            logger.info("encountered @import rule")
            href = None
            for tag in node.prelude:
                if isinstance(tag, URLToken):
                    href = tag.value
            css = _download_css(base_url, href)
            css = css.strip()
            logger.info('imported CSS (whitespaces trimmed) length: {}'.format(len(css)))
            add_as_is = len(css) == 0
            if not add_as_is:
                css_string += css
        if add_as_is:
            css_string += node.serialize()
    return css_string
Exemple #26
0
    def get_screenshot_as_base64(self):
        # type: () -> tp.Text
        """
        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('Done! Creating image object...')
            screenshot = image_utils.image_from_base64(screenshot64)

            # rotating
            if display_rotation == -90:
                screenshot64 = image_utils.get_base64(screenshot.rotate(90))
            logger.debug('Done! Rotating...')

        return screenshot64
def set_browser_size(driver, required_size):
    # type: (AnyWebDriver, ViewPort) -> bool

    retries_left = _RETRIES

    # set browser size for mobile devices isn't working
    if is_mobile_device(driver):
        return True

    while True:
        logger.info("Trying to set browser size to: " + str(required_size))
        set_window_size(driver, required_size)
        time.sleep(_SLEEP)
        current_size = get_window_size(driver)
        logger.info("Current browser size: " + str(required_size))

        if retries_left or current_size != required_size:
            break
        retries_left -= 1

    return current_size == required_size
def get_viewport_size_or_display_size(driver):
    logger.info("get_viewport_size_or_display_size()")

    try:
        return get_viewport_size(driver)
    except Exception as e:
        logger.info(
            "Failed to extract viewport size using Javascript: {}".format(
                str(e)))
    # If we failed to extract the viewport size using JS, will use the
    # window size instead.

    logger.info("Using window size as viewport size.")
    window_size = get_window_size(driver)
    width = window_size["width"]
    height = window_size["height"]
    try:
        if is_landscape_orientation(driver) and height > width:
            height, width = width, height
    except WebDriverException:
        # Not every WebDriver supports querying for orientation.
        pass
    logger.info("Done! Size {:d} x {:d}".format(width, height))
    return dict(width=width, height=height)
Exemple #29
0
    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
Exemple #30
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.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)
            logger.debug("Entire page has size as screenshot")
            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._position_provider.set_position(Point(part.left, part.top))
            # 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._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))

            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