def save_webdriver_logs_by_type(self, log_type, test_name): """Get webdriver logs of the specified type and write them to a log file :param log_type: browser, client, driver, performance, server, syslog, crashlog or logcat :param test_name: test that has generated these logs """ try: logs = self.driver_wrapper.driver.get_log(log_type) except Exception: return if len(logs) > 0: log_file_name = '{}_{}.txt'.format(get_valid_filename(test_name), log_type) log_file_name = os.path.join(DriverWrappersPool.logs_directory, log_file_name) with open(log_file_name, 'a+', encoding='utf-8') as log_file: driver_type = self.driver_wrapper.config.get('Driver', 'type') log_file.write( u"\n{} '{}' test logs with driver = {}\n\n".format( datetime.now(), test_name, driver_type)) for entry in logs: timestamp = datetime.fromtimestamp( float(entry['timestamp']) / 1000.).strftime('%Y-%m-%d %H:%M:%S.%f') log_file.write(u'{}\t{}\t{}\n'.format( timestamp, entry['level'], entry['message'].rstrip()))
def configure_visual_directories(cls, driver_info): """Configure screenshots, videos and visual directories :param driver_info: driver property value to name folders """ if cls.screenshots_directory is None: # Unique screenshots and videos directories date = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S') folder_name = '%s_%s' % (date, driver_info) if driver_info else date folder_name = get_valid_filename(folder_name) cls.screenshots_directory = os.path.join(cls.output_directory, 'screenshots', folder_name) cls.screenshots_number = 1 cls.videos_directory = os.path.join(cls.output_directory, 'videos', folder_name) cls.logs_directory = os.path.join(cls.output_directory, 'logs', folder_name) cls.videos_number = 1 # Unique visualtests directories cls.visual_output_directory = os.path.join(cls.output_directory, 'visualtests', folder_name) cls.visual_number = 1
def __init__(self, driver_wrapper=None, force=False): self.driver_wrapper = driver_wrapper if driver_wrapper else DriverWrappersPool.get_default_wrapper( ) self.force = force if not self.driver_wrapper.config.getboolean_optional( 'VisualTests', 'enabled') and not self.force: return if 'PerceptualEngine' not in globals(): raise Exception( 'The visual tests are enabled, but needle is not installed') self.utils = self.driver_wrapper.utils self.logger = logging.getLogger(__name__) self.output_directory = DriverWrappersPool.visual_output_directory # Update baseline with real platformVersion value if '{platformVersion}' in self.driver_wrapper.baseline_name: platform_version = self.driver_wrapper.driver.desired_capabilities[ 'platformVersion'] baseline_name = self.driver_wrapper.baseline_name.replace( '{platformVersion}', platform_version) self.driver_wrapper.baseline_name = baseline_name self.driver_wrapper.visual_baseline_directory = os.path.join( DriverWrappersPool.visual_baseline_directory, get_valid_filename(baseline_name)) self.baseline_directory = self.driver_wrapper.visual_baseline_directory self.engine = self._get_engine() self.save_baseline = self.driver_wrapper.config.getboolean_optional( 'VisualTests', 'save') # Create folders if not os.path.exists(self.baseline_directory): os.makedirs(self.baseline_directory) if not os.path.exists(self.output_directory): os.makedirs(self.output_directory) # Copy js, css and html template to output directory dst_template_path = os.path.join(self.output_directory, self.report_name) if not os.path.exists(dst_template_path): resources_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'resources') orig_template_path = os.path.join(resources_path, self.template_name) orig_javascript_path = os.path.join(resources_path, self.javascript_name) dst_javascript_path = os.path.join(self.output_directory, self.javascript_name) orig_css_path = os.path.join(resources_path, self.css_name) dst_css_path = os.path.join(self.output_directory, self.css_name) shutil.copyfile(orig_template_path, dst_template_path) shutil.copyfile(orig_javascript_path, dst_javascript_path) shutil.copyfile(orig_css_path, dst_css_path) self._add_summary_to_report()
def assert_screenshot(self, element, filename, file_suffix=None, threshold=0, exclude_elements=[]): """Assert that a screenshot of an element is the same as a screenshot on disk, within a given threshold :param element: either a WebElement, PageElement or element locator as a tuple (locator_type, locator_value). If None, a full screenshot is taken. :param filename: the filename for the screenshot, which will be appended with ``.png`` :param file_suffix: a string to be appended to the output filename with extra info about the test. :param threshold: percentage threshold for triggering a test failure (value between 0 and 1) :param exclude_elements: list of WebElements, PageElements or element locators as a tuple (locator_type, locator_value) that must be excluded from the assertion """ if not self.driver_wrapper.config.getboolean_optional('VisualTests', 'enabled') and not self.force: return if not (isinstance(threshold, int) or isinstance(threshold, float)) or threshold < 0 or threshold > 1: raise TypeError('Threshold must be a number between 0 and 1: {}'.format(threshold)) # Search elements web_element = self.utils.get_web_element(element) exclude_web_elements = [] for exclude_element in exclude_elements: try: exclude_web_elements.append(self.utils.get_web_element(exclude_element)) except NoSuchElementException as e: self.logger.warning("Element to be excluded not found: %s", str(e)) baseline_file = os.path.join(self.baseline_directory, '{}.png'.format(filename)) filename_with_suffix = '{0}__{1}'.format(filename, file_suffix) if file_suffix else filename unique_name = '{0:0=2d}_{1}'.format(DriverWrappersPool.visual_number, filename_with_suffix) unique_name = '{}.png'.format(get_valid_filename(unique_name)) output_file = os.path.join(self.output_directory, unique_name) report_name = '{}<br>({})'.format(file_suffix, filename) if file_suffix else '-<br>({})'.format(filename) # Get screenshot and modify it img = Image.open(BytesIO(self.driver_wrapper.driver.get_screenshot_as_png())) img = self.remove_scrolls(img) img = self.mobile_resize(img) img = self.exclude_elements(img, exclude_web_elements) img = self.crop_element(img, web_element) img.save(output_file) DriverWrappersPool.visual_number += 1 # Determine whether we should save the baseline image if self.save_baseline or not os.path.exists(baseline_file): # Copy screenshot to baseline shutil.copyfile(output_file, baseline_file) if self.driver_wrapper.config.getboolean_optional('VisualTests', 'complete_report'): self._add_result_to_report('baseline', report_name, output_file, None, 'Screenshot added to baseline') self.logger.debug("Visual screenshot '%s' saved in visualtests/baseline folder", filename) else: # Compare the screenshots self.compare_files(report_name, output_file, baseline_file, threshold)
def configure_visual_baseline(self): """Configure baseline directory""" # Get baseline name baseline_name = self.config.get_optional('VisualTests', 'baseline_name', '{Driver_type}') for section in self.config.sections(): for option in self.config.options(section): option_value = self.config.get(section, option) baseline_name = baseline_name.replace('{{{0}_{1}}}'.format(section, option), option_value) # Configure baseline directory if baseline name has changed if self.baseline_name != baseline_name: self.baseline_name = baseline_name self.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory, get_valid_filename(baseline_name))
def _download_video(self, video_url, video_name): """Download a video from the remote node :param video_url: video url :param video_name: video name """ filename = '{0:0=2d}_{1}'.format(DriverWrappersPool.videos_number, video_name) filename = '{}.mp4'.format(get_valid_filename(filename)) filepath = os.path.join(DriverWrappersPool.videos_directory, filename) if not os.path.exists(DriverWrappersPool.videos_directory): os.makedirs(DriverWrappersPool.videos_directory) response = requests.get(video_url) open(filepath, 'wb').write(response.content) self.logger.info("Video saved in '%s'", filepath) DriverWrappersPool.videos_number += 1
def capture_screenshot(self, name): """Capture screenshot and save it in screenshots folder :param name: screenshot name suffix :returns: screenshot path """ filename = '{0:0=2d}_{1}'.format(DriverWrappersPool.screenshots_number, name) filename = '{}.png'.format(get_valid_filename(filename)) filepath = os.path.join(DriverWrappersPool.screenshots_directory, filename) if not os.path.exists(DriverWrappersPool.screenshots_directory): os.makedirs(DriverWrappersPool.screenshots_directory) if self.driver_wrapper.driver.get_screenshot_as_file(filepath): self.logger.info('Screenshot saved in %s', filepath) DriverWrappersPool.screenshots_number += 1 return filepath return None
def configure_visual_baseline(self): """Configure baseline directory""" # Get baseline name baseline_name = self.config.get_optional('VisualTests', 'baseline_name', '{Driver_type}') for section in self.config.sections(): for option in self.config.options(section): option_value = self.config.get(section, option) baseline_name = baseline_name.replace( '{{{0}_{1}}}'.format(section, option), option_value) # Configure baseline directory if baseline name has changed if self.baseline_name != baseline_name: self.baseline_name = baseline_name self.visual_baseline_directory = os.path.join( DriverWrappersPool.visual_baseline_directory, get_valid_filename(baseline_name))
def configure_visual_directories(cls, driver_info): """Configure screenshots, videos and visual directories :param driver_info: driver property value to name folders """ if cls.screenshots_directory is None: # Unique screenshots and videos directories date = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S') folder_name = '%s_%s' % (date, driver_info) if driver_info else date folder_name = get_valid_filename(folder_name) cls.screenshots_directory = os.path.join(cls.output_directory, 'screenshots', folder_name) cls.screenshots_number = 1 cls.videos_directory = os.path.join(cls.output_directory, 'videos', folder_name) cls.videos_number = 1 # Unique visualtests directories cls.visual_output_directory = os.path.join(cls.output_directory, 'visualtests', folder_name) cls.visual_number = 1
def __init__(self, driver_wrapper=None, force=False): self.driver_wrapper = driver_wrapper if driver_wrapper else DriverWrappersPool.get_default_wrapper() self.force = force if not self.driver_wrapper.config.getboolean_optional('VisualTests', 'enabled') and not self.force: return if 'PerceptualEngine' not in globals(): raise Exception('The visual tests are enabled, but needle is not installed') self.utils = self.driver_wrapper.utils self.logger = logging.getLogger(__name__) self.output_directory = DriverWrappersPool.visual_output_directory # Update baseline with real platformVersion value if '{platformVersion}' in self.driver_wrapper.baseline_name: platform_version = self.driver_wrapper.driver.desired_capabilities['platformVersion'] baseline_name = self.driver_wrapper.baseline_name.replace('{platformVersion}', platform_version) self.driver_wrapper.baseline_name = baseline_name self.driver_wrapper.visual_baseline_directory = os.path.join(DriverWrappersPool.visual_baseline_directory, get_valid_filename(baseline_name)) self.baseline_directory = self.driver_wrapper.visual_baseline_directory self.engine = self._get_engine() self.save_baseline = self.driver_wrapper.config.getboolean_optional('VisualTests', 'save') # Create folders if not os.path.exists(self.baseline_directory): os.makedirs(self.baseline_directory) if not os.path.exists(self.output_directory): os.makedirs(self.output_directory) # Copy js, css and html template to output directory dst_template_path = os.path.join(self.output_directory, self.report_name) if not os.path.exists(dst_template_path): resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'resources') orig_template_path = os.path.join(resources_path, self.template_name) orig_javascript_path = os.path.join(resources_path, self.javascript_name) dst_javascript_path = os.path.join(self.output_directory, self.javascript_name) orig_css_path = os.path.join(resources_path, self.css_name) dst_css_path = os.path.join(self.output_directory, self.css_name) shutil.copyfile(orig_template_path, dst_template_path) shutil.copyfile(orig_javascript_path, dst_javascript_path) shutil.copyfile(orig_css_path, dst_css_path) self._add_summary_to_report()
def download_videos(cls, name, test_passed=True, maintain_default=False): """Download saved videos if video is enabled or if test fails :param name: destination file name :param test_passed: True if the test has passed :param maintain_default: True if the default driver should not be closed """ # Exclude first wrapper if the driver must be reused driver_wrappers = cls.driver_wrappers[ 1:] if maintain_default else cls.driver_wrappers video_name = '{}_driver{}' if len(driver_wrappers) > 1 else '{}' video_name = video_name if test_passed else 'error_{}'.format( video_name) driver_index = 1 for driver_wrapper in driver_wrappers: if not driver_wrapper.driver: continue try: # Download video if necessary (error case or enabled video) if (not test_passed or driver_wrapper.config.getboolean_optional('Server', 'video_enabled', False)) \ and driver_wrapper.remote_node_video_enabled: if driver_wrapper.server_type in ['ggr', 'selenoid']: name = get_valid_filename( video_name.format(name, driver_index)) Selenoid(driver_wrapper).download_session_video(name) elif driver_wrapper.server_type == 'grid': # Download video from Grid Extras driver_wrapper.utils.download_remote_video( driver_wrapper.remote_node, driver_wrapper.session_id, video_name.format(name, driver_index)) except Exception as exc: # Capture exceptions to avoid errors in teardown method due to session timeouts driver_wrapper.logger.warn('Error downloading videos: %s' % exc) driver_index += 1
def save_all_ggr_logs(cls, test_name, test_passed): """Get all GGR logs of each driver and write them to log files :param test_name: test that has generated these logs :param test_passed: True if the test has passed """ log_name = '{} [driver {}]' if len(cls.driver_wrappers) > 1 else '{}' driver_index = 1 for driver_wrapper in cls.driver_wrappers: if not driver_wrapper.driver or driver_wrapper.server_type not in [ 'ggr', 'selenoid' ]: continue try: if driver_wrapper.config.getboolean_optional( 'Server', 'logs_enabled') or not test_passed: name = get_valid_filename( log_name.format(test_name, driver_index)) Selenoid(driver_wrapper).download_session_log(name) except Exception as exc: # Capture exceptions to avoid errors in teardown method due to session timeouts driver_wrapper.logger.warn('Error downloading GGR logs: %s' % exc) driver_index += 1
def test_get_valid_filename_length(): input_filename = ' hola:pep /ito* ' expected_filename = 'hola_pep__it' valid_filename = format_utils.get_valid_filename(input_filename, 12) assert expected_filename == valid_filename
def test_get_valid_filename(input_filename, expected_filename): valid_filename = format_utils.get_valid_filename(input_filename) assert expected_filename == valid_filename
def assert_screenshot(self, element, filename, file_suffix=None, threshold=0, exclude_elements=[]): """Assert that a screenshot of an element is the same as a screenshot on disk, within a given threshold :param element: either a WebElement, PageElement or element locator as a tuple (locator_type, locator_value). If None, a full screenshot is taken. :param filename: the filename for the screenshot, which will be appended with ``.png`` :param file_suffix: a string to be appended to the output filename with extra info about the test. :param threshold: percentage threshold for triggering a test failure (value between 0 and 1) :param exclude_elements: list of WebElements, PageElements or element locators as a tuple (locator_type, locator_value) that must be excluded from the assertion """ if not self.driver_wrapper.config.getboolean_optional( 'VisualTests', 'enabled') and not self.force: return if not (isinstance(threshold, int) or isinstance( threshold, float)) or threshold < 0 or threshold > 1: raise TypeError( 'Threshold must be a number between 0 and 1: {}'.format( threshold)) # Search elements web_element = self.utils.get_web_element(element) exclude_web_elements = [] for exclude_element in exclude_elements: try: exclude_web_elements.append( self.utils.get_web_element(exclude_element)) except NoSuchElementException as e: self.logger.warning("Element to be excluded not found: %s", str(e)) baseline_file = os.path.join(self.baseline_directory, '{}.png'.format(filename)) filename_with_suffix = '{0}__{1}'.format( filename, file_suffix) if file_suffix else filename unique_name = '{0:0=2d}_{1}'.format(DriverWrappersPool.visual_number, filename_with_suffix) unique_name = '{}.png'.format(get_valid_filename(unique_name)) output_file = os.path.join(self.output_directory, unique_name) report_name = '{}<br>({})'.format( file_suffix, filename) if file_suffix else '-<br>({})'.format(filename) # Get screenshot and modify it img = Image.open( BytesIO(self.driver_wrapper.driver.get_screenshot_as_png())) img = self.remove_scrolls(img) img = self.mobile_resize(img) img = self.exclude_elements(img, exclude_web_elements) img = self.crop_element(img, web_element) img.save(output_file) DriverWrappersPool.visual_number += 1 # Determine whether we should save the baseline image if self.save_baseline or not os.path.exists(baseline_file): # Copy screenshot to baseline shutil.copyfile(output_file, baseline_file) if self.driver_wrapper.config.getboolean_optional( 'VisualTests', 'complete_report'): self._add_result_to_report('baseline', report_name, output_file, None, 'Screenshot added to baseline') self.logger.debug( "Visual screenshot '%s' saved in visualtests/baseline folder", filename) else: # Compare the screenshots self.compare_files(report_name, output_file, baseline_file, threshold)