def poll_render_status(self, render_request): # type: (RenderRequest) -> List[RenderStatusResults] iterations = 0 statuses = [] # type: List[RenderStatusResults] if not render_request.render_id: raise EyesError("RenderStatus: Got empty renderId!") fails_count = 0 finished = False while True: if finished: break while True: try: statuses = self.eyes_connector.render_status_by_id( render_request.render_id) except Exception as e: logger.exception(e) datetime_utils.sleep(1000) fails_count += 1 finally: iterations += 1 datetime_utils.sleep(500) if statuses or 0 < fails_count < 3: break finished = bool(statuses and statuses[0] is not None and (statuses[0].status != RenderStatus.RENDERING or iterations > self.MAX_ITERATIONS or False)) if statuses[0].status == RenderStatus.ERROR: raise EyesError("Got error during rendering: \n\t{}".format( statuses[0].error)) return statuses
def add_running_test(self, running_test): # type: (RunningTest) -> int if running_test in self.running_tests: raise EyesError("The running test {} already exists in the render " "task".format(running_test)) self.running_tests.append(running_test) return len(self.running_tests) - 1
def _check_image(self, region_provider, name, ignore_mismatch, check_settings): # type: (RegionProvider, Text, bool, ImagesCheckSettings) -> bool # Set the title to be linked to the screenshot. self._raw_title = name if name else "" if not self._is_opened: self.abort() raise EyesError("you must call open() before checking") image = check_settings.values.image # type: Image.Image if not isinstance(self.cut_provider, NullCutProvider): logger.debug("cutting...") image = self.cut_provider.cut(image) self.debug_screenshot_provider.save(image, "cut") self._screenshot = EyesImagesScreenshot(image) if not self.configure.viewport_size: self._set_viewport_size( RectangleSize(width=image.width, height=image.height)) check_settings = check_settings.timeout(0) match_result = self._check_window_base(region_provider, self._raw_title, ignore_mismatch, check_settings) self._screenshot = None self._raw_title = None return match_result.as_expected
def _run_render_status_requests(self): status_tasks = [] while True: with self._have_status_tasks: while not (self._shutdown or status_tasks or self._status_tasks): self._have_status_tasks.wait() if self._shutdown: break new_tasks, self._status_tasks = self._status_tasks, [] status_tasks.extend(new_tasks) ids = [t.render_id for t in status_tasks] logger.debug("render_status_by_id call with ids: {}".format(ids)) try: statuses = self._server_connector.render_status_by_id(*ids) except Exception as e: for task in status_tasks: task.on_error(e) continue rendering = [] current_time = time() for status, task in zip(statuses, status_tasks): if status.status == RenderStatus.RENDERING: if current_time > task.timeout_time: task.on_error(EyesError("Render time out")) else: rendering.append(task) else: task.on_success(status) status_tasks = rendering if status_tasks: datetime_utils.sleep(1000, "Render status polling delay")
def _run_render_requests(self): while True: with self._have_render_tasks: while not (self._shutdown or self._render_tasks): self._have_render_tasks.wait() if self._shutdown: break render_tasks, self._render_tasks = self._render_tasks, [] requests = [r.request for r in render_tasks] logger.debug("render call with requests: {}".format(requests)) try: responses = self._server_connector.render(*requests) except Exception as e: for task in render_tasks: task.on_error(e) continue statuses = [] for task, r in zip(render_tasks, responses): if (r.render_status == RenderStatus.NEED_MORE_RESOURCE or r.need_more_dom): task.on_error(EyesError("Some resources aren't uploaded")) else: statuses.append( _Status(r.render_id, task.on_success, task.on_error)) with self._have_status_tasks: self._status_tasks.extend(statuses) self._have_status_tasks.notify()
def perform(self): # type: () -> RenderStatusResults def get_and_put_resource(url): # type: (str) -> VGResource resource = self.request_resources.get(url) self.eyes_connector.render_put_resource(running_render, resource) return resource requests = [] rq = self.prepare_data_for_rg(self.script) requests.append(rq) fetch_fails = 0 render_requests = None while True: try: render_requests = self.eyes_connector.render(*requests) except Exception as e: logger.exception(e) fetch_fails += 1 time.sleep(1.5) if not render_requests: continue running_render = render_requests[0] rq.render_id = running_render.render_id need_more_dom = running_render.need_more_dom need_more_resources = ( running_render.render_status == RenderStatus.NEED_MORE_RESOURCE ) still_running = ( need_more_resources or need_more_dom or fetch_fails > self.MAX_FAILS_COUNT ) dom_resource = rq.dom.resource if need_more_resources: for url in running_render.need_more_resources: self.put_cache.fetch_and_store(url, get_and_put_resource) if need_more_dom: self.eyes_connector.render_put_resource(running_render, dom_resource) if not still_running: break statuses = self.poll_render_status(rq) if statuses and statuses[0].status == RenderStatus.ERROR: raise EyesError( "Render failed for {} with the message: {}".format( statuses[0].status, statuses[0].error ) ) return statuses[0]
def render_status(self): # type: ()->RenderStatusResults status = self._render_statuses.get(self._current_uuid) logger.debug("task.uuid: {}, status: {}".format( self._current_uuid, status)) if not status: raise EyesError("Got empty render status with key {}!".format( self._current_uuid)) return status
def prepare_rg_requests(self, dom, request_resources): # type: (RGridDom, Dict) -> List[RenderRequest] if self.size_mode == "region" and self.region_to_check is None: raise EyesError("Region to check should be present") if self.size_mode == "selector" and not isinstance( self.selector, VisualGridSelector): raise EyesError("Selector should be present") requests = [] region = None for running_test in self.running_tests: if self.region_to_check: region = dict( x=self.region_to_check.x, y=self.region_to_check.y, width=self.region_to_check.width, height=self.region_to_check.height, ) r_info = RenderInfo.from_( size_mode=self.size_mode, selector=self.selector, region=region, render_browser_info=running_test.browser_info, ) requests.append( RenderRequest( webhook=self.rendering_info.results_url, agent_id=self.agent_id, url=dom.url, stitching_service=self.rendering_info. stitching_service_url, dom=dom, resources=request_resources, render_info=r_info, browser_name=running_test.browser_info.browser, platform_name=running_test.browser_info.platform, script_hooks=self.script_hooks, selectors_to_find_regions_for=list( chain(*self.region_selectors)), send_dom=running_test.configuration.send_dom, options=self.request_options, )) return requests
def get_current_position(self): # type: () -> Point """ The scroll position of the current frame. """ if self._driver.is_mobile_web: x, y = self._driver.execute_script( self._JS_GET_CURRENT_SCROLL_POSITION, self._scroll_root_element ) if x is None or y is None: raise EyesError("Got None as scroll position! ({},{})".format(x, y)) return Point(x, y) return self.get_current_position_static(self._driver, self._scroll_root_element)
def get_script_result(self, dont_fetch_resources): # type: (bool) -> Dict logger.debug("get_script_result(dont_fetch_resources={})".format( dont_fetch_resources)) try: return dom_snapshot_script.create_dom_snapshot( self.driver, dont_fetch_resources, None, DOM_EXTRACTION_TIMEOUT, self.configure.enable_cross_origin_rendering, ) except dom_snapshot_script.DomSnapshotFailure as e: raise_from(EyesError("Failed to capture dom snapshot"), e)
def prepare_rg_requests(self, running_test, dom, request_resources): # type: (RunningTest, RGridDom, Dict) -> RenderRequest if self.size_mode == "region" and self.region_to_check is None: raise EyesError("Region to check should be present") if self.size_mode == "selector" and not isinstance( self.selector, VisualGridSelector): raise EyesError("Selector should be present") region = None if self.region_to_check: region = dict( x=self.region_to_check.x, y=self.region_to_check.y, width=self.region_to_check.width, height=self.region_to_check.height, ) r_info = RenderInfo( width=running_test.browser_info.width, height=running_test.browser_info.height, size_mode=self.size_mode, selector=self.selector, region=region, emulation_info=running_test.browser_info.emulation_info, ) return RenderRequest( webhook=self.rendering_info.results_url, agent_id=self.agent_id, url=dom.url, dom=dom, resources=request_resources, render_info=r_info, browser_name=running_test.browser_info.browser_type, platform=running_test.browser_info.platform, script_hooks=self.script_hooks, selectors_to_find_regions_for=list(chain(*self.region_selectors)), send_dom=running_test.configuration.send_dom, )
def check(self, name, check_settings): # type: (Text, SeleniumCheckSettings) -> MatchResult """ Takes a snapshot and matches it with the expected output. :param name: The name of the tag. :param check_settings: target which area of the window to check. :return: The match results. """ if self.configuration.is_disabled: return MatchResult() if not self.is_opened: self.abort() raise EyesError("you must call open() before checking") return self._current_eyes.check(name, check_settings)
def create_position_provider(driver, stitch_mode, scroll_root_element, target_element): # type: (EyesWebDriver,StitchMode,EyesWebElement,Optional[EyesWebElement])->PositionProvider logger.debug( "initializing position provider. stitch_mode: {}".format(stitch_mode)) if stitch_mode == StitchMode.Scroll: return ScrollPositionProvider(driver, scroll_root_element) elif stitch_mode == StitchMode.CSS: if (driver.user_agent.os == OSNames.IOS and driver.user_agent.browser == BrowserNames.MobileSafari): return CSSMobileSafariPositionProvider(driver, scroll_root_element, target_element) return CSSTranslatePositionProvider(driver, scroll_root_element) else: raise EyesError("Wrong sitch_mode")
def _init_driver(self, driver): # type: (AnyWebDriver) -> None 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: self._driver = EyesWebDriver(driver, self) if self._driver.is_mobile_app and not isinstance(driver, AppiumWebDriver): logger.error("To test a mobile app you need to use appium webdriver") if Feature.SCALE_MOBILE_APP in self.configure.features: raise EyesError( "For mobile app testing the appium driver should be used" )
def check(self, check_settings, name=None): if isinstance(name, SeleniumCheckSettings) or isinstance( check_settings, basestring): check_settings, name = name, check_settings if check_settings is None: check_settings = Target.window() if name: check_settings = check_settings.with_name(name) if self.configure.is_disabled: logger.info("check(): ignored (disabled)") return MatchResult() if not self.is_open: self.abort() raise EyesError("you must call open() before checking") return self._current_eyes.check(check_settings)
def _close(self, raise_ex, wait_result): # type: (bool, bool) -> Optional[TestResults] if self.configure.is_disabled: return None if not self.is_open: raise EyesError("Eyes not open") results = self._commands.eyes_close_eyes(self._eyes_ref, wait_result) self._eyes_ref = None self._driver = None if wait_result: results = demarshal_test_results(results, self.configure) for r in results: log_session_results_and_raise_exception(raise_ex, r) return results[0] # Original interface returns just one result else: return None
def close(self, raise_ex=True): # noqa # type: (Optional[bool]) -> Optional[TestResults] if self.configuration.is_disabled: logger.debug("close(): ignored (disabled)") return TestResults() if not self.test_list: return TestResults() logger.debug("VisualGridEyes.close()\n\t test_list %s" % self.test_list) self.close_async() while True: states = list(set([t.state for t in self.test_list])) if len(states) == 1 and states[0] == "completed": break datetime_utils.sleep(500) self._is_opened = False for test in self.test_list: if test.pending_exceptions: raise EyesError( "During test execution above exception raised. \n {:s}". join(str(e) for e in test.pending_exceptions)) if raise_ex: for test in self.test_list: if test.test_result is None: raise TestFailedError("Test haven't finished correctly") results = test.test_result scenario_id_or_name = results.name app_id_or_name = results.app_name if results.is_unresolved and not results.is_new: raise DiffsFoundError(results, scenario_id_or_name, app_id_or_name) if results.is_new: raise NewTestError(results, scenario_id_or_name, app_id_or_name) if results.is_failed: raise TestFailedError(results, scenario_id_or_name, app_id_or_name) all_results = [t.test_result for t in self.test_list if t.test_result] if not all_results: return TestResults() return all_results[0]
def get_all_test_results(self, should_raise_exception=True, timeout=DEFAULT_ALL_TEST_RESULTS_TIMEOUT): # type: (bool, Optional[int]) -> TestResultsSummary try: results = self._commands.manager_close_all_eyes(self._ref, timeout) except TimeoutError: raise EyesError( "Tests didn't finish in {} seconds".format(timeout)) # We don't have server_url, api_key and proxy settings in runner # USDK should return them back as a part of TestResults structured_results = demarshal_test_results(results, None) for r in structured_results: log_session_results_and_raise_exception(should_raise_exception, r) return TestResultsSummary([ TestResultContainer(result, None, None) for result in structured_results ])
def _set_viewport_size(self): viewport_size = self.configure.viewport_size if viewport_size is None: for render_bi in self.configure.browsers_info: if not render_bi.emulation_info: viewport_size = RectangleSize(render_bi.width, render_bi.height) break if viewport_size is None: viewport_size = self._get_viewport_size() self.configure.viewport_size = viewport_size try: eyes_selenium_utils.set_viewport_size(self.driver, viewport_size) except Exception as e: logger.exception(e) raise EyesError(str(e))
def poll_render_status(self, requests): # type: (List[RenderRequest]) -> List[RenderStatusResults] logger.debug("poll_render_status call with Requests {}".format(requests)) iterations = 0 statuses = [] # type: List[RenderStatusResults] fails_count = 0 finished = False while True: if finished: break while True: try: statuses = self.eyes_connector.render_status_by_id( *[rq.render_id for rq in requests] ) except Exception as e: logger.exception(e) datetime_utils.sleep( 1000, msg="/render-status throws exception... sleeping for 1s" ) fails_count += 1 finally: iterations += 1 datetime_utils.sleep(1500, msg="Rendering...") if iterations > self.MAX_ITERATIONS: raise EyesError( "Max iterations in poll_render_status has been reached " "for render_id: \n {}".format( "\n".join(s.render_id for s in statuses) ) ) if statuses or 0 < fails_count < 3: break finished = bool( statuses and ( all(s.status != RenderStatus.RENDERING for s in statuses) or iterations > self.MAX_ITERATIONS or False ) ) return statuses
def set_viewport_size(driver, required_size): # noqa # type: (AnyWebDriver, ViewPort) -> None actual_viewport_size = get_viewport_size(driver) if actual_viewport_size != required_size: actual_viewport_size = set_browser_size_by_viewport_size( driver, actual_viewport_size, required_size) if actual_viewport_size != required_size: # Additional attempt. This Solves the "non-resizable maximized browser" case # Attempt to fix it by minimizing window and retrying again logger.info("Trying workaround with minimization...") try: driver.minimize_window() actual_viewport_size = set_browser_size_by_viewport_size( driver, actual_viewport_size, required_size) except WebDriverException: logger.warning("minimize_window failed") if actual_viewport_size != required_size: raise EyesError("Failed to set the viewport size.") else: logger.info("Required viewport size is already set")
def _set_viewport_size(self): viewport_size = self.configure.viewport_size if viewport_size is None: for render_bi in self.configure.browsers_info: if isinstance(render_bi, DesktopBrowserInfo) or isinstance( render_bi, RenderBrowserInfo): viewport_size = render_bi.viewport_size break if viewport_size is None: self.configure.viewport_size = self._get_viewport_size() return self.configure.viewport_size = viewport_size try: eyes_selenium_utils.set_viewport_size(self.driver, viewport_size) except Exception as e: logger.exception(e) raise EyesError(str(e))
def _set_viewport_size(self): viewport_size = self.configure.viewport_size if viewport_size is None: for render_bi in self.configure.browsers_info: if isinstance(render_bi, (DesktopBrowserInfo, RenderBrowserInfo)): viewport_size = render_bi.viewport_size break if viewport_size is None: self.configure.viewport_size = self._get_viewport_size() return self.configure.viewport_size = viewport_size try: eyes_selenium_utils.set_viewport_size(self.driver, viewport_size) except Exception as e: self.logger.exception("set_viewport_size failure") raise_from(EyesError("Failed to set viewport size"), e)
def get_script_result(self): # type: () -> Dict script_response = {} status = None timer = self._start_timer() while True: if status == "SUCCESS" or self.is_check_timer_timeout: self.is_check_timer_timeout = False break script_result_string = self.driver.execute_script( PROCESS_RESOURCES + "return __processPageAndPoll();") try: script_response = json.loads(script_result_string, object_pairs_hook=OrderedDict) status = script_response.get("status") except Exception as e: logger.exception(e) timer.cancel() script_result = script_response.get("value") if script_result is None or status == "ERROR": raise EyesError("Failed to capture script_result") return script_result
def check(self, check_settings, name=None): # type: (SeleniumCheckSettings, Optional[Text]) -> Optional[MatchResult] if isinstance(name, SeleniumCheckSettings) or isinstance( check_settings, string_types ): check_settings, name = name, check_settings if check_settings is None: check_settings = Target.window() if name: check_settings = check_settings.with_name(name) if self.configure.is_disabled: return None if not self.is_open: self.abort() raise EyesError("you must call open() before checking") results = self._commands.eyes_check( self._eyes_ref, marshal_check_settings(_is_selenium_driver(self.driver), check_settings), self._marshaled_configuration(), ) if results: results = demarshal_match_result(results) if ( not results.as_expected and self.configure.failure_reports is FailureReports.IMMEDIATE ): raise TestFailedError( "Mismatch found in '{}' of '{}'".format( self.configure.test_name, self.configure.app_name ) ) else: return results else: return None
def get_dom_script_result(driver, dom_extraction_timeout, timer_name, script_for_run): # type: (AnyWebDriver, int, Text, Text) -> Dict is_check_timer_timeout = [] script_response = {} status = None def start_timer(): def set_timer(): is_check_timer_timeout.append(True) timer = threading.Timer(datetime_utils.to_sec(dom_extraction_timeout), set_timer) timer.daemon = True timer.setName(timer_name) timer.start() return timer timer = start_timer() while True: if status == "SUCCESS" or is_check_timer_timeout: del is_check_timer_timeout[:] break script_result_string = driver.execute_script(script_for_run) try: script_response = json.loads(script_result_string, object_pairs_hook=OrderedDict) status = script_response.get("status") except Exception as e: logger.exception(e) datetime_utils.sleep(1000, "Waiting for the end of DOM extraction") timer.cancel() script_result = script_response.get("value") if script_result is None or status == "ERROR": raise EyesError("Failed to capture script_result") return script_result
def default_content_scroll_position(self): # type: () -> tp.Union[Point, EyesError] if len(self) == 0: raise EyesError("No frames in frame chain") result = self[0].parent_scroll_position return Point(result.x, result.y)
def _validate_frame_window(self): # type: () -> None if self.frame_window.width <= 0 or self.frame_window.height <= 0: raise EyesError("Got empty frame window for screenshot!")
def perform(self): # noqa # type: () -> List[RenderStatusResults] def get_and_put_resource(url, running_render): # type: (str, RunningRender) -> VGResource logger.debug( "get_and_put_resource({0}, render_id={1}) call".format( url, running_render.render_id)) resource = self.full_request_resources.get(url) self.eyes_connector.render_put_resource(running_render, resource) return resource requests = self.prepare_data_for_rg(self.script) fetch_fails = 0 render_requests = None already_force_putted = False while True: try: self.put_cache.process_all() render_requests = self.eyes_connector.render(*requests) except Exception: logger.exception( "During rendering for requests {}".format(requests)) fetch_fails += 1 datetime_utils.sleep( 1500, msg="/render throws exception... sleeping for 1.5s") if fetch_fails > self.MAX_FAILS_COUNT: raise EyesError( "Render is failed. Max count retries reached for {}". format(requests)) if not render_requests: logger.error("running_renders is null") continue need_more_dom = need_more_resources = False for i, running_render in enumerate(render_requests): requests[i].render_id = running_render.render_id need_more_dom = running_render.need_more_dom need_more_resources = (running_render.render_status == RenderStatus.NEED_MORE_RESOURCE) get_and_put_resource_wtih_render = partial( get_and_put_resource, running_render=running_render) dom_resource = requests[i].dom.resource if self.is_force_put_needed and not already_force_putted: for url in self.full_request_resources: self.put_cache.fetch_and_store( url, get_and_put_resource_wtih_render, force=True) already_force_putted = True if need_more_resources: for url in running_render.need_more_resources: self.put_cache.fetch_and_store( url, get_and_put_resource_wtih_render) if need_more_dom: self.eyes_connector.render_put_resource( running_render, dom_resource) still_running = (need_more_resources or need_more_dom or fetch_fails > self.MAX_FAILS_COUNT) if not still_running: break return self.poll_render_status(requests)
def set_viewport_size(driver, required_size): # noqa # type: (AnyWebDriver, ViewPort) -> None actual_viewport_size = get_viewport_size(driver) if actual_viewport_size == required_size: logger.info("Required viewport size already set") return None logger.info( "Actual Viewport Size: {}\n\tTrying to set viewport size to: {}". format(str(actual_viewport_size), str(required_size))) try: # We move the window to (0,0) to have the best chance to be able to # set the viewport size as requested. driver.set_window_position(0, 0) except WebDriverException: logger.warning("Failed to move the browser window to (0,0)") if set_browser_size_by_viewport_size(driver, actual_viewport_size, required_size): return None # Additional attempt. This Solves the "maximized browser" bug # (border size for maximized browser sometimes different than # non-maximized, so the original browser size calculation is # wrong). logger.info("Trying workaround for maximization...") if set_browser_size_by_viewport_size(driver, actual_viewport_size, required_size): return None width_diff = abs(actual_viewport_size["width"] - required_size["width"]) width_step = -1 if width_diff > 0 else 1 # -1 for smaller size, 1 for larger height_diff = abs(actual_viewport_size["height"] - required_size["height"]) height_step = -1 if height_diff > 0 else 1 browser_size = get_window_size(driver) curr_width_change = 0 curr_height_change = 0 if width_diff <= _MAX_DIFF and height_diff <= _MAX_DIFF: logger.info("Trying workaround for zoom...") last_required_browser_size = None while (abs(curr_width_change) <= width_diff and abs(curr_height_change) <= height_diff): if abs(curr_width_change) <= width_diff: curr_width_change += width_step if abs(curr_height_change) <= height_diff: curr_height_change += height_step required_browser_size = dict( width=browser_size["width"] + curr_width_change, height=browser_size["height"] + curr_height_change, ) if required_browser_size == last_required_browser_size: logger.info( "Browser size is as required but viewport size does not match!" ) logger.info("Browser size: {}, Viewport size: {}".format( required_browser_size, actual_viewport_size)) logger.info("Stopping viewport size attempts.") break set_browser_size(driver, required_browser_size) last_required_browser_size = required_browser_size actual_viewport_size = get_viewport_size(driver) logger.info( "Current viewport size: {}".format(actual_viewport_size)) if actual_viewport_size == required_size: return None else: logger.info("Zoom workaround failed.") # Attempt to fix by minimizing window logger.info("Trying workaround for minimization...") try: # some webdriver's don't support minimize_window driver.minimize_window() except WebDriverException as e: logger.exception(e) if set_browser_size_by_viewport_size(driver, actual_viewport_size, required_size): return None logger.error("Minimization workaround failed.") raise EyesError("Failed to set the viewport size.")