def __attrs_post_init__(self): # type: () -> None self._screenshot_type = update_screenshot_type( self._screenshot_type, self._image, self._driver ) position_provider = eyes_selenium_utils.get_cur_position_provider(self._driver) if not self._driver.is_mobile_app: self._frame_chain = self._driver.frame_chain.clone() frame_size = self.get_frame_size(position_provider) self._current_frame_scroll_position = eyes_selenium_utils.get_updated_scroll_position( # noqa position_provider ) self.updated_frame_location_in_screenshot( self._frame_location_in_screenshot ) logger.debug("Calculating frame window...") self.frame_window = Region.from_( self._frame_location_in_screenshot, frame_size ) else: self._frame_chain = FrameChain() self._current_frame_scroll_position = Point.ZERO() self._frame_location_in_screenshot = Point.ZERO() self.frame_window = Region.from_( self._frame_location_in_screenshot, self.image ) self.frame_window = self.frame_window.intersect(Region.from_(self.image))
def from_screenshot(cls, driver, image, screenshot_region): # type: (EyesWebDriver, Image.Image, Region) -> EyesWebDriverScreenshot return cls( driver, image, ScreenshotType.ENTIRE_FRAME, Point.ZERO(), frame_window=Region.from_(Point.ZERO(), screenshot_region.size), region_window=Region.from_(screenshot_region), )
def get_image(self): image = TakesScreenshotImageProvider(self._eyes).get_image() if self._eyes.is_cut_provider_explicitly_set: return image scale_ratio = self._eyes.device_pixel_ratio original_viewport_size = self._eyes._get_viewport_size() viewport_size = original_viewport_size.scale(scale_ratio) logger.info("logical viewport size: {}".format(original_viewport_size)) force_full_page_screenshot = self._eyes.configure.force_full_page_screenshot if force_full_page_screenshot is not None: if not force_full_page_screenshot: current_frame_chain = self._eyes.driver.frame_chain # type: FrameChain if len(current_frame_chain) == 0: position_provider = ScrollPositionProvider( self._eyes.driver, self._eyes.driver.find_element_by_tag_name("html"), ) loc = position_provider.get_current_position() else: loc = current_frame_chain.default_content_scroll_position loc = loc.scale(scale_ratio) image = image_utils.crop_image( image, Region.from_(loc, viewport_size)) return image
def intersected_region(self, region, coordinates_type): # type: (Region, CoordinatesType) -> Region argument_guard.not_none(region) argument_guard.not_none(coordinates_type) if region.is_size_empty: return Region.from_(region) original_coordinates_type = region.coordinates_type intersected_region = self.convert_region_location( region, original_coordinates_type, self.SCREENSHOT_AS_IS ) # If the request was context based, we intersect with the frame window. if original_coordinates_type in [self.CONTEXT_AS_IS, self.CONTEXT_RELATIVE]: intersected_region = intersected_region.intersect(self.frame_window) # If the request is screenshot based, we intersect with the image elif original_coordinates_type == self.SCREENSHOT_AS_IS: intersected_region = intersected_region.intersect( Region(0, 0, self.image.width, self.image.height) ) else: raise ValueError( "Unknown coordinates type: '%s'" % original_coordinates_type ) # If the intersection is empty we don't want to convert the coordinates. if intersected_region.is_size_empty: return intersected_region # Converting the result to the required coordinates type intersected_region = self.convert_region_location( intersected_region, self.SCREENSHOT_AS_IS, coordinates_type ) return intersected_region
def _get_region_in_screenshot(self, region, image, pixel_ratio): # type: (Region, Image, float) -> Region if region.is_size_empty: return region logger.info("Creating screenshot object...") # We need the screenshot to be able to convert the region to screenshot coordinates. screenshot = self.screenshot_factory.make_screenshot( image ) # type: EyesWebDriverScreenshot logger.info("Getting region in screenshot...") region_in_screenshot = screenshot.intersected_region( region, CoordinatesType.SCREENSHOT_AS_IS ) logger.info("Region in screenshot: {}".format(region_in_screenshot)) region_in_screenshot = region_in_screenshot.scale(pixel_ratio) logger.info("Scaled region: {}".format(region_in_screenshot)) region_in_screenshot = self.region_position_compensation.compensate_region_position( region_in_screenshot, pixel_ratio ) # Handling a specific case where the region is actually larger than # the screenshot (e.g., when body width/height are set to 100%, and # an internal div is set to value which is larger than the viewport). region_in_screenshot.intersect(Region.from_(image)) logger.info("Region after intersect: {}".format(region_in_screenshot)) return region_in_screenshot
def demarshal_locate_result(results): # type: (dict) -> Dict[Text, List[APIRegion]] return { locator_id: [APIRegion.from_(region) for region in regions] if regions else [] for locator_id, regions in results.items() }
def _ensure_frame_visible(self): logger.debug("scroll_root_element_: {}".format(self._scroll_root_element)) current_fc = self.driver.frame_chain.clone() fc = self.driver.frame_chain.clone() self.driver.execute_script("window.scrollTo(0,0);") origin_driver = eyes_selenium_utils.get_underlying_driver(self.driver) while len(fc) > 0: logger.debug("fc count: {}".format(fc.size)) self.driver.switch_to.parent_frame_static(origin_driver.switch_to, fc) self.driver.execute_script("window.scrollTo(0,0);") child_frame = fc.pop() parent_frame = fc.peek scroll_root_element = None if fc.size == self._original_fc.size: logger.debug("PositionProvider: {}".format(self.position_provider)) self._position_memento = self.position_provider.get_state() scroll_root_element = self._scroll_root_element else: if parent_frame: scroll_root_element = parent_frame.scroll_root_element if not scroll_root_element: scroll_root_element = self.driver.find_element_by_tag_name("html") logger.debug("scroll_root_element {}".format(scroll_root_element)) position_provider = self._element_position_provider_from( scroll_root_element ) position_provider.set_position(child_frame.location) reg = Region.from_(Point.ZERO(), child_frame.inner_size) self._effective_viewport.intersect(reg) self.driver.switch_to.frames(current_fc) return current_fc
def create_entire_frame(cls, driver, image, entire_frame_size): # type: (EyesWebDriver, Image.Image, RectangleSize) -> EyesWebDriverScreenshot return cls( driver, image, ScreenshotType.ENTIRE_FRAME, frame_location_in_screenshot=Point(0, 0), current_frame_scroll_position=Point(0, 0), frame_window=Region.from_(Point(0, 0), entire_frame_size), )
def from_screenshot(cls, driver, image, screenshot_region, frame_location_in_parent_screenshot): # type: (EyesWebDriver, Image.Image, Region, Point) -> EyesWebDriverScreenshot return cls( driver, image, ScreenshotType.ENTIRE_FRAME, frame_location_in_parent_screenshot - screenshot_region.location, frame_window=Region.from_(Point.ZERO(), screenshot_region.size), )
def _get_viewport_scroll_bounds(self): switch_to = self.driver.switch_to with switch_to.frames_and_back(self._original_fc): try: location = eyes_selenium_utils.get_current_position( self.driver, self.scroll_root_element) except WebDriverException as e: logger.warning(str(e)) logger.info("Assuming position is 0,0") location = Point(0, 0) viewport_bounds = Region.from_(location, self._get_viewport_size()) return viewport_bounds
def _region_from_element(element, screenshot): location = element.location size = element.size if screenshot: # Element's coordinates are context relative, so we need to convert them first. adjusted_location = screenshot.location_in_screenshot( Point(location["x"], location["y"]), CoordinatesType.CONTEXT_RELATIVE) else: adjusted_location = Point(location["x"], location["y"]) region = Region.from_(adjusted_location, size) return region
def create_entire_element( cls, driver, # type: EyesWebDriver image, # type: Image.Image entire_element_size, # type: RectangleSize frame_location_in_screenshot, # type: Point ): # type: (...) -> EyesWebDriverScreenshot return cls( driver, image, ScreenshotType.ENTIRE_FRAME, frame_location_in_screenshot, current_frame_scroll_position=Point(0, 0), frame_window=Region.from_(Point(0, 0), entire_element_size), )
def _floating_provider_from(self, region, bounds): if isinstance(region, Region): logger.debug("floating: FloatingRegionByRectangle") return FloatingRegionByRectangle(Region.from_(region), bounds) raise TypeError("Unknown region type.")
def _check_element(self, name, check_settings): element = self._target_element # type: EyesWebElement scroll_root_element = eyes_selenium_utils.curr_frame_scroll_root_element( self.driver, self._scroll_root_element) pos_provider = self._create_position_provider(scroll_root_element) self._region_to_check = Region.EMPTY() self._full_region_to_check = Region.EMPTY() result = None with eyes_selenium_utils.get_and_restore_state(pos_provider): with self._ensure_element_visible(element): pl = element.location try: self._check_frame_or_element = True display_style = element.get_computed_style("display") if self.configure.hide_scrollbars: element.hide_scrollbars() size_and_borders = element.size_and_borders border_widths = size_and_borders.borders element_size = size_and_borders.size use_entire_size = False if display_style != "inline" and ( element_size["height"] <= self._effective_viewport["height"] and element_size["width"] <= self._effective_viewport["width"]): self._element_position_provider = ElementPositionProvider( self.driver, element) use_entire_size = True else: self._element_position_provider = None element_region = Region( pl["x"] + border_widths["left"], pl["y"] + border_widths["top"], element_size["width"], element_size["height"], coordinates_type=CoordinatesType.SCREENSHOT_AS_IS, ) self._region_to_check = element_region if use_entire_size: self._full_region_to_check = Region.from_( element_region.location, self._element_position_provider.get_entire_size(), ) else: self._full_region_to_check = Region( left=element_region.left, top=element_region.top, width=element_region.width, height=element_region.height, ) if not self._effective_viewport.is_size_empty: self._region_to_check = self._region_to_check.intersect( self._effective_viewport) result = self._check_window_base(NULL_REGION_PROVIDER, name, False, check_settings) except Exception as e: logger.exception(e) raise e finally: if self.configure.hide_scrollbars: element.return_to_original_overflow() self._check_frame_or_element = False self._region_to_check = None self._element_position_provider = None return result
def get_stitched_region(self, region, full_area, position_provider): # type: (Region, Optional[Region], Optional[PositionProvider]) -> Image.Image argument_guard.not_none(region) argument_guard.not_none(position_provider) logger.info( "region: %s ; full_area: %s ; position_provider: %s" % (region, full_area, position_provider.__class__.__name__) ) origin_state = self.origin_provider.get_state() if self.origin_provider != position_provider: self.origin_provider.set_position( Point.ZERO() ) # first scroll to 0,0 so CSS stitching works. # Saving the original position (in case we were already in the outermost frame). original_stitched_state = position_provider.get_state() datetime_utils.sleep(self.wait_before_screenshots) initial_screenshot = self.image_provider.get_image() initial_size = RectangleSize.from_(initial_screenshot) pixel_ratio = self._get_pixel_ratio(initial_screenshot) scaled_cut_provider = self.cut_provider.scale(pixel_ratio) cutted_initial_screenshot = self._cut_if_needed( initial_screenshot, scaled_cut_provider ) self.debug_screenshot_provider.save( cutted_initial_screenshot, self._debug_msg("cutted_initial_screenshot") ) region_in_initial_screenshot = self._get_region_in_screenshot( region, cutted_initial_screenshot, pixel_ratio ) cropped_initial_screenshot = self._crop_if_needed( cutted_initial_screenshot, region_in_initial_screenshot ) self.debug_screenshot_provider.save( cropped_initial_screenshot, self._debug_msg("cropped_initial_screenshot") ) scaled_initial_screenshot = image_utils.scale_image( cropped_initial_screenshot, self.scale_provider ) self.debug_screenshot_provider.save( scaled_initial_screenshot, self._debug_msg("scaled_initial_screenshot") ) if full_area is None or full_area.is_empty: entire_size = self._get_entire_size(initial_screenshot, position_provider) # Notice that this might still happen even if we used # "get_image_part", since "entire_page_size" might be that of a # frame if ( scaled_initial_screenshot.width >= entire_size.width and scaled_initial_screenshot.height >= entire_size.height ): self.origin_provider.restore_state(origin_state) return scaled_initial_screenshot full_area = Region.from_(Point.ZERO(), entire_size) scaled_cropped_location = full_area.location physical_crop_location = Point.from_(scaled_cropped_location).scale(pixel_ratio) if region_in_initial_screenshot.is_empty: physical_crop_size = RectangleSize( initial_size.width - physical_crop_location.x, initial_size.height - physical_crop_location.y, ) source_region = Region.from_(physical_crop_location, physical_crop_size) else: # Starting with the screenshot we already captured at (0,0). source_region = region_in_initial_screenshot scaled_cropped_source_rect = self.cut_provider.to_region(source_region.size) scaled_cropped_source_rect = scaled_cropped_source_rect.offset( source_region.left, source_region.top ) scaled_cropped_source_region = dict( x=int(math.ceil(scaled_cropped_source_rect.left / pixel_ratio)), y=int(math.ceil(scaled_cropped_source_rect.top / pixel_ratio)), width=int(math.ceil(scaled_cropped_source_rect.width / pixel_ratio)), height=int(math.ceil(scaled_cropped_source_rect.height / pixel_ratio)), ) scaled_cropped_size = dict( width=scaled_cropped_source_region["width"], height=scaled_cropped_source_region["height"], ) # Getting the list of viewport regions composing the page # (we'll take screenshot for each one). if region_in_initial_screenshot.is_empty: x = max(0, full_area.left) y = max(0, full_area.top) w = min(full_area.width, scaled_cropped_size["width"]) h = min(full_area.height, scaled_cropped_size["height"]) rect_in_initial_screenshot = Region( round(x * pixel_ratio), round(y * pixel_ratio), round(w * pixel_ratio), round(h * pixel_ratio), ) else: rect_in_initial_screenshot = region_in_initial_screenshot screenshot_parts = self._get_image_parts( full_area, scaled_cropped_size, pixel_ratio, rect_in_initial_screenshot ) # Starting with element region size part of the screenshot. Use it as a size # template. stitched_image = Image.new("RGBA", (full_area.width, full_area.height)) # Take screenshot and stitch for each screenshot part. stitched_image = self._stitch_screenshot( original_stitched_state, position_provider, screenshot_parts, stitched_image, self.scale_provider.scale_ratio, scaled_cut_provider, ) position_provider.restore_state(original_stitched_state) self.origin_provider.restore_state(origin_state) return stitched_image