from inspect import isclass
import os
import time
from jsmin import jsmin
from navmazing import Navigate, NavigateStep
from selenium.common.exceptions import NoSuchElementException
from widgetastic.browser import Browser, DefaultPlugin
from cached_property import cached_property
from fixtures.pytest_store import store
from cfme import exceptions
from cfme.utils.browser import manager
from cfme.utils.log import logger, create_sublogger
from cfme.utils.wait import wait_for
from . import Implementation
[docs]class MiqSSUIBrowser(Browser):
def __init__(self, selenium, endpoint, extra_objects=None):
extra_objects = extra_objects or {}
extra_objects.update({
'appliance': endpoint.owner,
'endpoint': endpoint,
'store': store,
})
super(MiqSSUIBrowser, self).__init__(
selenium,
plugin_class=MiqSSUIBrowserPlugin,
logger=create_sublogger('MiqSSUIBrowser'),
extra_objects=extra_objects)
self.window_handle = selenium.current_window_handle
@property
def appliance(self):
return self.extra_objects['appliance']
[docs] def create_view(self, *args, **kwargs):
return self.appliance.ssui.create_view(*args, **kwargs)
@property
def product_version(self):
return self.appliance.version
[docs]class MiqSSUIBrowserPlugin(DefaultPlugin):
ENSURE_PAGE_SAFE = jsmin('''
function checkProgressBar() {
try {
return $('#ngProgress').attr('style').indexOf('width: 0%') > -1;
} catch(err) {
// Not ready yet
return false;
}
}
function checkJquery() {
if(typeof $ == 'undefined') {
return true;
} else {
return !($.active > 0);
}
}
return checkProgressBar() && checkJquery();''')
[docs] def ensure_page_safe(self, timeout='20s'):
# THIS ONE SHOULD ALWAYS USE JAVASCRIPT ONLY, NO OTHER SELENIUM INTERACTION
def _check():
result = self.browser.execute_script(self.ENSURE_PAGE_SAFE, silent=True)
# TODO: Logging
return bool(result)
wait_for(_check, timeout=timeout, delay=2, silent_failure=True, very_quiet=True)
[docs]class SSUINavigateStep(NavigateStep):
VIEW = None
@cached_property
def view(self):
if self.VIEW is None:
raise AttributeError('{} does not have VIEW specified'.format(type(self).__name__))
return self.create_view(self.VIEW, additional_context={'object': self.obj})
@property
def appliance(self):
return self.obj.appliance
[docs] def create_view(self, *args, **kwargs):
return self.appliance.ssui.create_view(*args, **kwargs)
[docs] def am_i_here(self):
try:
return self.view.is_displayed
except (AttributeError, NoSuchElementException):
return False
[docs] def pre_navigate(self, *args, **kwargs):
self.appliance.browser.open_browser(url_key=self.obj.appliance.server.address())
[docs] def do_nav(self, _tries=0, *args, **kwargs):
"""Describes how the navigation should take place."""
try:
self.step(*args, **kwargs)
except Exception as e:
logger.error(e)
raise
self.go(_tries, *args, **kwargs)
[docs] def log_message(self, msg, level="debug"):
class_name = self.obj.__name__ if isclass(self.obj) else self.obj.__class__.__name__
str_msg = "[SUI-NAV/{}/{}]: {}".format(class_name, self._name, msg)
getattr(logger, level)(str_msg)
[docs] def construct_message(self, here, resetter, view, duration, waited):
str_here = "Already Here" if here else "Needed Navigation"
str_resetter = "Resetter Used" if resetter else "No Resetter"
str_view = "View Returned" if view else "No View Available"
str_waited = "Waited on View" if waited else "No Wait on View"
return "{}/{}/{}/{} (elapsed {}ms)".format(
str_here, str_resetter, str_view, str_waited, duration
)
[docs] def go(self, _tries=0, *args, **kwargs):
nav_args = {'use_resetter': True, 'wait_for_view': False}
self.log_message("Beginning SUI Navigation...", level="info")
start_time = time.time()
if _tries > 2:
# Need at least three tries:
# 1: login_admin handles an alert or CannotContinueWithNavigation appears.
# 2: Everything should work. If not, NavigationError.
raise exceptions.NavigationError(self._name)
_tries += 1
for arg in nav_args:
if arg in kwargs:
nav_args[arg] = kwargs.pop(arg)
self.pre_navigate(_tries, *args, **kwargs)
here = False
resetter_used = False
waited = False
try:
here = self.am_i_here()
except Exception as e:
self.log_message(
"Exception raised [{}] whilst checking if already here".format(e), level="error")
if not here:
self.log_message("Prerequisite Needed")
self.prerequisite_view = self.prerequisite()
self.do_nav(_tries, *args, **kwargs)
if nav_args['use_resetter']:
resetter_used = True
self.resetter()
self.post_navigate(_tries)
view = self.view if self.VIEW is not None else None
duration = int((time.time() - start_time) * 1000)
if view and nav_args['wait_for_view'] and not os.environ.get(
'DISABLE_NAVIGATE_ASSERT', False):
waited = True
wait_for(
lambda: view.is_displayed, num_sec=10,
message="Waiting for view [{}] to display".format(view.__class__.__name__)
)
self.log_message(
self.construct_message(here, resetter_used, view, duration, waited), level="info"
)
return view
navigator = Navigate()
navigate_to = navigator.navigate
[docs]class ViaSSUI(Implementation):
def __str__(self):
return 'SSUI'
@cached_property
def widgetastic(self):
"""This gives us a widgetastic browser."""
# TODO: Make this a property that could watch for browser change?
browser = self.open_browser(url_key=self.appliance.server.address())
wt = MiqSSUIBrowser(browser, self)
manager.add_cleanup(self._reset_cache)
return wt