def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver = selenium.webdriver.Firefox() self.conf = config.get_config() else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver = selenium.webdriver.Firefox() self.conf = config.get_config() else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
class StacksPage(basepage.BaseNavigationPage): DEFAULT_TEMPLATE_SOURCE = 'raw' CONFIG = config.get_config() DEFAULT_PASSWORD = CONFIG.identity.admin_password STACKS_TABLE_NAME_COLUMN = 'name' STACKS_TABLE_STATUS_COLUMN = 'stack_status' def __init__(self, driver, conf): super(StacksPage, self).__init__(driver, conf) self._page_title = "Stacks" @property def stacks_table(self): return StacksTable(self.driver, self.conf) def _get_row_with_stack_name(self, name): return self.stacks_table.get_row(self.STACKS_TABLE_NAME_COLUMN, name) def create_stack(self, stack_name, template_data, template_source=DEFAULT_TEMPLATE_SOURCE, environment_source=None, environment_upload=None, timeout_mins=None, enable_rollback=None, password=DEFAULT_PASSWORD): select_template_form = self.stacks_table.select_template() select_template_form.template_source.value = template_source select_template_form.template_data.text = template_data select_template_form.submit() launch_stack_form = self.stacks_table.launch_stack() launch_stack_form.stack_name.text = stack_name launch_stack_form.password.text = password launch_stack_form.submit() def delete_stack(self, name): row = self._get_row_with_stack_name(name) row.mark() confirm_delete_stacks_form = self.stacks_table.delete_stack() confirm_delete_stacks_form.submit() def is_stack_present(self, name): return bool(self._get_row_with_stack_name(name)) def is_stack_create_complete(self, name): def cell_getter(): row = self._get_row_with_stack_name(name) return row and row.cells[self.STACKS_TABLE_STATUS_COLUMN] return bool( self.stacks_table.wait_cell_status(cell_getter, 'Create Complete')) def is_stack_deleted(self, name): return self.stacks_table.is_row_deleted( lambda: self._get_row_with_stack_name(name))
class BaseTestCase(testtools.TestCase): CONFIG = config.get_config() def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) # workaround for memory leak in Xvfb taken from: http://blog. # jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html self.vdisplay.xvfb_cmd.append("-noreset") # disables X access control self.vdisplay.xvfb_cmd.append("-ac") self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = webdriver.WebDriverWrapper() self.driver.maximize_window() self.driver.implicitly_wait(self.CONFIG.selenium.implicit_wait) self.driver.set_page_load_timeout( self.CONFIG.selenium.page_timeout) self.addOnException(self._dump_page_html_source) else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp() def _dump_page_html_source(self, exc_info): content = None try: pg_source = self._get_page_html_source() content = testtools.content.Content( testtools.content_type.ContentType('text', 'html'), lambda: pg_source) except Exception: exc_traceback = traceback.format_exc() content = testtools.content.text_content(exc_traceback) finally: self.addDetail("PageHTMLSource.html", content) def _get_page_html_source(self): """Gets html page source. self.driver.page_source is not used on purpose because it does not display html code generated/changed by javascript. """ html_elem = self.driver.find_element_by_tag_name("html") return html_elem.get_attribute("innerHTML").encode("UTF-8") def tearDown(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver.quit() if hasattr(self, 'vdisplay'): self.vdisplay.stop() super(BaseTestCase, self).tearDown()
def actual_decoration(obj): skip_method = _get_skip_method(obj) # get available services from configuration avail_services = config.get_config().service_available for req_service in req_services: if not getattr(avail_services, req_service, False): obj = skip_method(obj, "%s service is required for this test" " to work properly." % req_service) break return obj
def _parse_compound_config_option_value(option_name): """Parses the value of a given config option where option's section name is separated from option name by '.'. """ name_parts = option_name.split('.') name_parts.reverse() option = config.get_config() while name_parts: option = getattr(option, name_parts.pop()) return option
def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = selenium.webdriver.Firefox() self.conf = config.get_config() else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = selenium.webdriver.Firefox() self.conf = config.get_config() else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = webdriver.WebDriverWrapper() self.driver.maximize_window() self.conf = config.get_config() self.driver.implicitly_wait(self.conf.dashboard.page_timeout) else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
def actual_decoration(obj): # make sure that we can decorate method and classes as well if inspect.isclass(obj): if not _is_test_cls(obj): raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) skip_method = _mark_class_skipped else: if not _is_test_method_name(obj.__name__): raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) skip_method = _mark_method_skipped # get available services from configuration avail_services = config.get_config().service_available for req_service in req_services: if not getattr(avail_services, req_service, False): obj = skip_method(obj, "%s service is required for this test" " to work properly." % req_service) break return obj
def actual_decoration(obj): # make sure that we can decorate method and classes as well if inspect.isclass(obj): if not _is_test_cls(obj): raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) skip_method = _mark_class_skipped else: if not _is_test_method_name(obj.__name__): raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) skip_method = _mark_method_skipped # get available services from configuration avail_services = config.get_config().service_available for req_service in req_services: if not getattr(avail_services, req_service, False): obj = skip_method(obj, "%s service is required for this test" " to work properly." % req_service) break return obj
def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) # workaround for memory leak in Xvfb taken from: http://blog. # jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html self.vdisplay.xvfb_cmd.append("-noreset") # disables X access control self.vdisplay.xvfb_cmd.append("-ac") self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = webdriver.WebDriverWrapper() self.driver.maximize_window() self.conf = config.get_config() self.driver.implicitly_wait(self.conf.selenium.implicit_wait) self.driver.set_page_load_timeout(self.conf.selenium.page_timeout) else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp()
class BaseTestCase(testtools.TestCase): CONFIG = config.get_config() def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1920, height=1080) args = [] # workaround for memory leak in Xvfb taken from: # http://blog.jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html args.append("-noreset") # disables X access control args.append("-ac") if hasattr(self.vdisplay, 'extra_xvfb_args'): # xvfbwrapper 0.2.8 or newer self.vdisplay.extra_xvfb_args.extend(args) else: self.vdisplay.xvfb_cmd.extend(args) self.vdisplay.start() # Increase the default Python socket timeout from nothing # to something that will cope with slow webdriver startup times. # This *just* affects the communication between this test process # and the webdriver. socket.setdefaulttimeout(60) # Start the Selenium webdriver and setup configuration. desired_capabilities = dict(webdriver.desired_capabilities) desired_capabilities['loggingPrefs'] = {'browser': 'ALL'} self.driver = webdriver.WebDriverWrapper( desired_capabilities=desired_capabilities) if self.CONFIG.selenium.maximize_browser: self.driver.maximize_window() self.driver.implicitly_wait(self.CONFIG.selenium.implicit_wait) self.driver.set_page_load_timeout( self.CONFIG.selenium.page_timeout) self.addOnException(self._dump_page_html_source) self.addOnException(self._dump_browser_log) self.addOnException(self._save_screenshot) else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp() @contextlib.contextmanager def exceptions_captured(self, label): contents = [] try: yield contents except Exception: exc_traceback = traceback.format_exc() contents.append(testtools.content.text_content(exc_traceback)) finally: self.addDetail(label, contents[0]) @staticmethod def _unwrap_browser_log(_log): def rec(log): if isinstance(log, dict): return log['message'].encode('utf-8') elif isinstance(log, list): return '\n'.join([rec(item) for item in log]) else: return log.encode('utf-8') return rec(_log) def _dump_browser_log(self, exc_info): with self.exceptions_captured("BrowserLog.text") as contents: log = self.driver.get_log('browser') contents.append( testtools.content.Content( testtools.content_type.UTF8_TEXT, lambda: self._unwrap_browser_log(log))) def _dump_page_html_source(self, exc_info): with self.exceptions_captured("PageHTMLSource.html") as contents: pg_source = self._get_page_html_source() contents.append( testtools.content.Content( testtools.content_type.ContentType('text', 'html'), lambda: pg_source)) def zoom_out(self, times=3): """Zooming out prevents different elements being driven out of xvfb viewport (which in Selenium>=2.50.1 prevents interaction with them. """ html = self.driver.find_element(by.By.TAG_NAME, 'html') html.send_keys(keys.Keys.NULL) zoom_out_keys = (keys.Keys.SUBTRACT, ) * times action_chains.ActionChains(self.driver).key_down( keys.Keys.CONTROL).send_keys(*zoom_out_keys).key_up( keys.Keys.CONTROL).perform() def _save_screenshot(self, exc_info): with self.exceptions_captured("Screenshot") as contents: filename = self._get_screenshot_filename() self.driver.get_screenshot_as_file(filename) contents.append(testtools.content.text_content(filename)) def _get_screenshot_filename(self): screenshot_dir = os.path.join( ROOT_PATH, self.CONFIG.selenium.screenshots_directory) if not os.path.exists(screenshot_dir): os.makedirs(screenshot_dir) date_string = datetime.datetime.now().strftime('%Y.%m.%d-%H%M%S') test_name = self._testMethodName name = '%s_%s.png' % (test_name, date_string) return os.path.join(screenshot_dir, name) def _get_page_html_source(self): """Gets html page source. self.driver.page_source is not used on purpose because it does not display html code generated/changed by javascript. """ html_elem = self.driver.find_element_by_tag_name("html") return html_elem.get_attribute("innerHTML").encode("UTF-8") def tearDown(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver.quit() if hasattr(self, 'vdisplay'): self.vdisplay.stop() super(BaseTestCase, self).tearDown()
class BaseTestCase(testtools.TestCase): CONFIG = config.get_config() def setUp(self): if os.environ.get('INTEGRATION_TESTS', False): # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) # workaround for memory leak in Xvfb taken from: http://blog. # jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html self.vdisplay.xvfb_cmd.append("-noreset") # disables X access control self.vdisplay.xvfb_cmd.append("-ac") self.vdisplay.start() # Start the Selenium webdriver and setup configuration. self.driver = webdriver.WebDriverWrapper( logging_prefs={'browser': 'ALL'}) self.driver.maximize_window() self.driver.implicitly_wait(self.CONFIG.selenium.implicit_wait) self.driver.set_page_load_timeout( self.CONFIG.selenium.page_timeout) self.addOnException(self._dump_page_html_source) self.addOnException(self._dump_browser_log) self.addOnException(self._save_screenshot) else: msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) super(BaseTestCase, self).setUp() @staticmethod def _unwrap_browser_log(_log): def rec(log): if isinstance(log, dict): return log['message'].encode('utf-8') elif isinstance(log, list): return '\n'.join([rec(item) for item in log]) else: return log.encode('utf-8') return rec(_log) def _dump_browser_log(self, exc_info): content = None try: log = self.driver.get_log('browser') content = testtools.content.Content( testtools.content_type.UTF8_TEXT, lambda: self._unwrap_browser_log(log)) except Exception: exc_traceback = traceback.format_exc() content = testtools.content.text_content(exc_traceback) finally: self.addDetail("BrowserLog.text", content) def _dump_page_html_source(self, exc_info): content = None try: pg_source = self._get_page_html_source() content = testtools.content.Content( testtools.content_type.ContentType('text', 'html'), lambda: pg_source) except Exception: exc_traceback = traceback.format_exc() content = testtools.content.text_content(exc_traceback) finally: self.addDetail("PageHTMLSource.html", content) def _save_screenshot(self, exc_info): screenshot_dir = os.path.join( ROOT_PATH, self.CONFIG.selenium.screenshots_directory) if not os.path.exists(screenshot_dir): os.makedirs(screenshot_dir) date_string = datetime.datetime.now().strftime('%Y.%m.%d-%H%M%S') test_name = self._testMethodName name = '%s_%s.png' % (test_name, date_string) filename = os.path.join(screenshot_dir, name) self.driver.get_screenshot_as_file(filename) content = testtools.content.text_content(filename) self.addDetail("Screenshot", content) def _get_page_html_source(self): """Gets html page source. self.driver.page_source is not used on purpose because it does not display html code generated/changed by javascript. """ html_elem = self.driver.find_element_by_tag_name("html") return html_elem.get_attribute("innerHTML").encode("UTF-8") def tearDown(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver.quit() if hasattr(self, 'vdisplay'): self.vdisplay.stop() super(BaseTestCase, self).tearDown()
class BaseTestCase(testtools.TestCase): CONFIG = config.get_config() def setUp(self): self._configure_log() if not os.environ.get('INTEGRATION_TESTS', False): msg = "The INTEGRATION_TESTS env variable is not set." raise self.skipException(msg) # Start a virtual display server for running the tests headless. if os.environ.get('SELENIUM_HEADLESS', False): self.vdisplay = xvfbwrapper.Xvfb(width=1920, height=1080) args = [] # workaround for memory leak in Xvfb taken from: # http://blog.jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html args.append("-noreset") # disables X access control args.append("-ac") if hasattr(self.vdisplay, 'extra_xvfb_args'): # xvfbwrapper 0.2.8 or newer self.vdisplay.extra_xvfb_args.extend(args) else: self.vdisplay.xvfb_cmd.extend(args) self.vdisplay.start() # Increase the default Python socket timeout from nothing # to something that will cope with slow webdriver startup times. # This *just* affects the communication between this test process # and the webdriver. socket.setdefaulttimeout(60) # Start the Selenium webdriver and setup configuration. desired_capabilities = dict(webdriver.desired_capabilities) desired_capabilities['loggingPrefs'] = {'browser': 'ALL'} self.driver = webdriver.WebDriverWrapper( desired_capabilities=desired_capabilities ) if self.CONFIG.selenium.maximize_browser: self.driver.maximize_window() self.driver.implicitly_wait(self.CONFIG.selenium.implicit_wait) self.driver.set_page_load_timeout( self.CONFIG.selenium.page_timeout) self.addOnException(self._attach_page_source) self.addOnException(self._attach_screenshot) self.addOnException(self._attach_browser_log) self.addOnException(self._attach_test_log) super(BaseTestCase, self).setUp() def _configure_log(self): """Configure log to capture test logs include selenium logs in order to attach them if test will be broken. """ LOGGER.handlers[:] = [] # clear other handlers to set target handler self._log_buffer = StringIO() stream_handler = logging.StreamHandler(stream=self._log_buffer) stream_handler.setLevel(logging.DEBUG) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') stream_handler.setFormatter(formatter) LOGGER.addHandler(stream_handler) @property def _test_report_dir(self): report_dir = os.path.join(ROOT_PATH, 'test_reports', self._testMethodName) if not os.path.isdir(report_dir): os.makedirs(report_dir) return report_dir def _attach_page_source(self, exc_info): source_path = os.path.join(self._test_report_dir, 'page.html') with self.log_exception("Attach page source"): with open(source_path, 'w') as f: f.write(self._get_page_html_source()) def _attach_screenshot(self, exc_info): screen_path = os.path.join(self._test_report_dir, 'screenshot.png') with self.log_exception("Attach screenshot"): self.driver.get_screenshot_as_file(screen_path) def _attach_browser_log(self, exc_info): browser_log_path = os.path.join(self._test_report_dir, 'browser.log') with self.log_exception("Attach browser log"): with open(browser_log_path, 'w') as f: f.write( self._unwrap_browser_log(self.driver.get_log('browser'))) def _attach_test_log(self, exc_info): test_log_path = os.path.join(self._test_report_dir, 'test.log') with self.log_exception("Attach test log"): with open(test_log_path, 'w') as f: f.write(self._log_buffer.getvalue().encode('utf-8')) @contextlib.contextmanager def log_exception(self, label): try: yield except Exception: self.addDetail( label, testtools.content.text_content(traceback.format_exc())) @staticmethod def _unwrap_browser_log(_log): def rec(log): if isinstance(log, dict): return log['message'].encode('utf-8') elif isinstance(log, list): return '\n'.join([rec(item) for item in log]) else: return log.encode('utf-8') return rec(_log) def zoom_out(self, times=3): """Zooming out prevents different elements being driven out of xvfb viewport (which in Selenium>=2.50.1 prevents interaction with them. """ html = self.driver.find_element(by.By.TAG_NAME, 'html') html.send_keys(keys.Keys.NULL) zoom_out_keys = (keys.Keys.SUBTRACT,) * times action_chains.ActionChains(self.driver).key_down( keys.Keys.CONTROL).send_keys(*zoom_out_keys).key_up( keys.Keys.CONTROL).perform() def _get_page_html_source(self): """Gets html page source. self.driver.page_source is not used on purpose because it does not display html code generated/changed by javascript. """ html_elem = self.driver.find_element_by_tag_name("html") return html_elem.get_attribute("innerHTML").encode("utf-8") def tearDown(self): if os.environ.get('INTEGRATION_TESTS', False): self.driver.quit() if hasattr(self, 'vdisplay'): self.vdisplay.stop() super(BaseTestCase, self).tearDown()
class BaseTestCase(testtools.TestCase): CONFIG = config.get_config() def setUp(self): self._configure_log() self.addOnException( lambda exc_info: setattr(self, '_need_attach_test_log', True)) def cleanup(): if getattr(self, '_need_attach_test_log', None): self._attach_test_log() self.addCleanup(cleanup) width, height = SCREEN_SIZE display = '0.0' # Start a virtual display server for running the tests headless. if IS_SELENIUM_HEADLESS: width, height = 1920, 1080 self.vdisplay = xvfbwrapper.Xvfb(width=width, height=height) args = [] # workaround for memory leak in Xvfb taken from: # http://blog.jeffterrace.com/2012/07/xvfb-memory-leak-workaround.html args.append("-noreset") # disables X access control args.append("-ac") if hasattr(self.vdisplay, 'extra_xvfb_args'): # xvfbwrapper 0.2.8 or newer self.vdisplay.extra_xvfb_args.extend(args) else: self.vdisplay.xvfb_cmd.extend(args) self.vdisplay.start() display = self.vdisplay.new_display self.addCleanup(self.vdisplay.stop) self.video_recorder = VideoRecorder(width, height, display=display) self.video_recorder.start() self.addOnException( lambda exc_info: setattr(self, '_need_attach_video', True)) def cleanup(): self.video_recorder.stop() if getattr(self, '_need_attach_video', None): self._attach_video() else: self.video_recorder.clear() self.addCleanup(cleanup) # Increase the default Python socket timeout from nothing # to something that will cope with slow webdriver startup times. # This *just* affects the communication between this test process # and the webdriver. socket.setdefaulttimeout(60) # Start the Selenium webdriver and setup configuration. desired_capabilities = dict(webdriver.desired_capabilities) desired_capabilities['loggingPrefs'] = {'browser': 'ALL'} self.driver = webdriver.WebDriverWrapper( desired_capabilities=desired_capabilities) if self.CONFIG.selenium.maximize_browser: self.driver.maximize_window() if IS_SELENIUM_HEADLESS: # force full screen in xvfb self.driver.set_window_size(width, height) self.driver.implicitly_wait(self.CONFIG.selenium.implicit_wait) self.driver.set_page_load_timeout(self.CONFIG.selenium.page_timeout) self.addOnException(self._attach_page_source) self.addOnException(self._attach_screenshot) self.addOnException( lambda exc_info: setattr(self, '_need_attach_browser_log', True)) def cleanup(): if getattr(self, '_need_attach_browser_log', None): self._attach_browser_log() self.driver.quit() self.addCleanup(cleanup) super(BaseTestCase, self).setUp() def addOnException(self, exception_handler): def wrapped_handler(exc_info): if issubclass(exc_info[0], testtools.testcase.TestSkipped): return return exception_handler(exc_info) super(BaseTestCase, self).addOnException(wrapped_handler) def __hash__(self): return hash((type(self), self._testMethodName)) def _configure_log(self): """Configure log to capture test logs include selenium logs. This allows us to attach them if test will be broken. """ # clear other handlers to set target handler ROOT_LOGGER.handlers[:] = [] self._log_buffer = io.StringIO() stream_handler = logging.StreamHandler(stream=self._log_buffer) stream_handler.setLevel(logging.DEBUG) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') stream_handler.setFormatter(formatter) ROOT_LOGGER.addHandler(stream_handler) @property def _test_report_dir(self): report_dir = os.path.join( ROOT_PATH, 'test_reports', '{}.{}'.format(self.__class__.__name__, self._testMethodName)) if not os.path.isdir(report_dir): os.makedirs(report_dir) return report_dir def _attach_page_source(self, exc_info): source_path = os.path.join(self._test_report_dir, 'page.html') with self.log_exception("Attach page source"): with open(source_path, 'w') as f: f.write(self._get_page_html_source()) def _attach_screenshot(self, exc_info): screen_path = os.path.join(self._test_report_dir, 'screenshot.png') with self.log_exception("Attach screenshot"): self.driver.get_screenshot_as_file(screen_path) def _attach_video(self, exc_info=None): with self.log_exception("Attach video"): if not os.path.isfile(self.video_recorder.file_path): LOG.warning("Can't find video %s", self.video_recorder.file_path) return shutil.move(self.video_recorder.file_path, os.path.join(self._test_report_dir, 'video.mp4')) def _attach_browser_log(self, exc_info=None): browser_log_path = os.path.join(self._test_report_dir, 'browser.log') with self.log_exception("Attach browser log"): with open(browser_log_path, 'w') as f: f.write( self._unwrap_browser_log(self.driver.get_log('browser'))) def _attach_test_log(self, exc_info=None): test_log_path = os.path.join(self._test_report_dir, 'test.log') with self.log_exception("Attach test log"): with open(test_log_path, 'w') as f: f.write(self._log_buffer.getvalue().encode('utf-8')) @contextlib.contextmanager def log_exception(self, label): try: yield except Exception: self.addDetail( label, testtools.content.text_content(traceback.format_exc())) @staticmethod def _unwrap_browser_log(_log): def rec(log): if isinstance(log, dict): return log['message'].encode('utf-8') elif isinstance(log, list): return '\n'.join([rec(item) for item in log]) else: return log.encode('utf-8') return rec(_log) def zoom_out(self, times=3): """Zooming out a specified element. It prevents different elements being driven out of xvfb viewport (which in Selenium>=2.50.1 prevents interaction with them). """ html = self.driver.find_element(by.By.TAG_NAME, 'html') html.send_keys(keys.Keys.NULL) zoom_out_keys = (keys.Keys.SUBTRACT, ) * times action_chains.ActionChains(self.driver).key_down( keys.Keys.CONTROL).send_keys(*zoom_out_keys).key_up( keys.Keys.CONTROL).perform() def _get_page_html_source(self): """Gets html page source. self.driver.page_source is not used on purpose because it does not display html code generated/changed by javascript. """ html_elem = self.driver.find_element_by_tag_name("html") return html_elem.get_attribute("innerHTML").encode("utf-8")
# a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pytest from openstack_dashboard.test.integration_tests import config from openstack_dashboard.test.integration_tests import helpers from openstack_dashboard.test.integration_tests.regions import messages CONFIG = config.get_config() class TestVolumeSnapshotsBasic(helpers.TestCase): """Login as demo user""" VOLUME_NAME = helpers.gen_random_resource_name("volume") VOLUME_SNAPSHOT_NAME = helpers.gen_random_resource_name("volume_snapshot") @property def volumes_snapshot_page(self): return self.home_pg.go_to_project_volumes_snapshotspage() def setUp(self): """Setup: create volume""" super().setUp() volumes_page = self.home_pg.go_to_project_volumes_volumespage()
class Navigation(object): """Provide basic navigation among pages. * allows navigation among pages without need to import pageobjects * shortest path possible should be always used for navigation to specific page as user would navigate in the same manner * go_to_*some_page* method respects following pattern: * go_to_{sub_menu}_{pagename}page * go_to_*some_page* methods are generated at runtime at module import * go_to_*some_page* method returns pageobject * pages must be located in the correct directories for the navigation to work correctly * pages modules and class names must respect following pattern to work correctly with navigation module: * module name consist of menu item in lower case without spaces and '&' * page class begins with capital letter and ends with 'Page' Examples: In order to go to Project/Compute/Overview page, one would have to call method go_to_compute_overviewpage() In order to go to Admin/System/Overview page, one would have to call method go_to_system_overviewpage() """ # constants MAX_SUB_LEVEL = 4 MIN_SUB_LEVEL = 2 SIDE_MENU_MAX_LEVEL = 3 CONFIG = config.get_config() PAGES_IMPORT_PATH = ["openstack_dashboard.test.integration_tests.pages.%s"] if CONFIG.plugin.is_plugin and CONFIG.plugin.plugin_page_path: for path in CONFIG.plugin.plugin_page_path: PAGES_IMPORT_PATH.append(path + ".%s") ITEMS = "_" CORE_PAGE_STRUCTURE = \ { "Project": { "Compute": { ITEMS: ( "Overview", "Instances", "Images", "Key Pairs", "Server Groups", ) }, "Volumes": { ITEMS: ( "Volumes", "Backups", "Snapshots", "Consistency Groups", "Consistency Group Snapshots", ) }, "Network": { ITEMS: ( "Network Topology", "Networks", "Routers", "Security Groups", "Floating IPs", ) }, ITEMS: ( "API Access", ) }, "Admin": { "Compute": { ITEMS: ( "Hypervisors", "Host Aggregates", "Instances", "Flavors", "Images", ) }, "Volume": { ITEMS: ( "Volumes", "Snapshots", "Volume Types", ) }, "Network": { ITEMS: ( "Networks", "Routers", "Floating IPs", ) }, "System": { ITEMS: ( "Defaults", "Metadata Definitions", "System Information" ) }, ITEMS: ( "Overview", ) }, "Identity": { ITEMS: ( "Projects", "Users", "Groups", "Roles", "Application Credentials", ) }, "Settings": { ITEMS: ( "User Settings", "Change Password", ) }, } _main_content_locator = (by.By.ID, 'content_body') # protected methods def _go_to_page(self, path, page_class=None): """Go to page specified via path parameter. * page_class parameter overrides basic process for receiving pageobject """ path_len = len(path) if path_len < self.MIN_SUB_LEVEL or path_len > self.MAX_SUB_LEVEL: raise ValueError( "Navigation path length should be in the interval" " between %s and %s, but its length is %s" % (self.MIN_SUB_LEVEL, self.MAX_SUB_LEVEL, path_len)) if path_len == self.MIN_SUB_LEVEL: # menu items that do not contain second layer of menu if path[0] == "Settings": self._go_to_settings_page(path[1]) else: self._go_to_side_menu_page([path[0], None, path[1]]) else: # side menu contains only three sub-levels self._go_to_side_menu_page(path[:self.SIDE_MENU_MAX_LEVEL]) if path_len == self.MAX_SUB_LEVEL: # apparently there is tabbed menu, # because another extra sub level is present self._go_to_tab_menu_page(path[self.MAX_SUB_LEVEL - 1]) # if there is some nonstandard pattern in page object naming return self._get_page_class(path, page_class)(self.driver, self.conf) def _go_to_tab_menu_page(self, item_text): content_body = self.driver.find_element(*self._main_content_locator) content_body.find_element_by_link_text(item_text).click() def _go_to_settings_page(self, item_text): """Go to page that is located under the settings tab.""" self.topbar.user_dropdown_menu.click_on_settings() self.navaccordion.click_on_menu_items(third_level=item_text) def _go_to_side_menu_page(self, menu_items): """Go to page that is located in the side menu (navaccordion).""" self.navaccordion.click_on_menu_items(*menu_items) def _get_page_cls_name(self, filename): """Gather page class name from path. * take last item from path (should be python filename without extension) * make the first letter capital * append 'Page' """ cls_name = "".join((filename.capitalize(), "Page")) return cls_name def _get_page_class(self, path, page_cls_name): # last module name does not contain '_' final_module = self.unify_page_path(path[-1], preserve_spaces=False) page_cls_path = ".".join(path[:-1] + (final_module, )) page_cls_path = self.unify_page_path(page_cls_path) # append 'page' as every page module ends with this keyword page_cls_path += "page" page_cls_name = page_cls_name or self._get_page_cls_name(final_module) module = None # return imported class for path in self.PAGES_IMPORT_PATH: try: module = importlib.import_module(path % page_cls_path) break except ImportError: pass if module is None: raise ImportError("Failed to import module: " + (path % page_cls_path)) return getattr(module, page_cls_name) class GoToMethodFactory(object): """Represent the go_to_some_page method.""" METHOD_NAME_PREFIX = "go_to_" METHOD_NAME_SUFFIX = "page" METHOD_NAME_DELIMITER = "_" # private methods def __init__(self, path, page_class=None): self.path = path self.page_class = page_class self._name = self._create_name() def __call__(self, *args, **kwargs): return Navigation._go_to_page(args[0], self.path, self.page_class) # protected methods def _create_name(self): """Create method name. * consist of 'go_to_subsubmenu_menuitem_page' """ if len(self.path) < 3: path_2_name = list(self.path[-2:]) else: path_2_name = list(self.path[-3:]) name = self.METHOD_NAME_DELIMITER.join(path_2_name) name = self.METHOD_NAME_PREFIX + name + self.METHOD_NAME_SUFFIX name = Navigation.unify_page_path(name, preserve_spaces=False) return name # properties @property def name(self): return self._name # classmethods @classmethod def _initialize_go_to_methods(cls): """Create all navigation methods based on the PAGE_STRUCTURE.""" def rec(items, sub_menus): if isinstance(items, dict): for sub_menu, sub_item in items.items(): rec(sub_item, sub_menus + (sub_menu, )) elif isinstance(items, (list, tuple)): # exclude ITEMS element from sub_menus paths = (sub_menus[:-1] + (menu_item, ) for menu_item in items) for path in paths: cls._create_go_to_method(path) rec(cls.CORE_PAGE_STRUCTURE, ()) plugin_page_structure_strings = cls.CONFIG.plugin.plugin_page_structure for plugin_ps_string in plugin_page_structure_strings: plugin_page_structure = json.loads(plugin_ps_string) rec(plugin_page_structure, ()) @classmethod def _create_go_to_method(cls, path, class_name=None): go_to_method = Navigation.GoToMethodFactory(path, class_name) inst_method = six.create_unbound_method(go_to_method, Navigation) # TODO(e0ne): remove python2 support once all integration jobs # will be switched to python3. if six.PY3: def _go_to_page(self, path): return Navigation._go_to_page(self, path) wrapped_go_to = functools.partialmethod(_go_to_page, path) setattr(Navigation, inst_method.name, wrapped_go_to) else: setattr(Navigation, inst_method.name, inst_method) @classmethod def unify_page_path(cls, path, preserve_spaces=True): """Unify path to page. Replace '&' in path with 'and', remove spaces (if not specified otherwise) and convert path to lower case. """ path = path.replace("&", "and") path = path.lower() if preserve_spaces: path = path.replace(" ", "_") else: path = path.replace(" ", "") return path
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from functools import wraps import logging import os import subprocess import types from openstack_dashboard.test.integration_tests import config CONF = config.get_config() LOGGER = logging.getLogger(__name__) IS_SELENIUM_HEADLESS = os.environ.get('SELENIUM_HEADLESS', False) def once_only(func): called_funcs = {} @wraps(func) def wrapper(*args, **kwgs): if func.__name__ not in called_funcs: result = obj = func(*args, **kwgs) if isinstance(obj, types.GeneratorType): def gi_wrapper(): while True: