"""A set of functions for dealing with the toolbar buttons
The main CFME toolbar is accessed by using the Root and Sub titles of the buttons.
Usage:
tb = web_ui.toolbar
tb.select('Configuration', 'Add a New Host')
"""
import cfme.fixtures.pytest_selenium as sel
from selenium.webdriver.common.by import By
from cfme.exceptions import ToolbarOptionGreyedOrUnavailable
from cfme.utils import version
from cfme.utils.log import logger
from xml.sax.saxutils import quoteattr, unescape
[docs]def xpath_quote(x):
"""Putting strings in xpath requires unescape also"""
# TODO: Move it to some library!
return unescape(quoteattr(x))
[docs]def root_loc(root):
""" Returns the locator of the root button
Args:
root: The string name of the button.
Returns: A locator for the root button.
"""
return (By.XPATH,
("//div[contains(@class, 'dhx_toolbar_btn')][contains(@title, {0})] | "
"//div[contains(@class, 'dhx_toolbar_btn')][contains(@data-original-title, {0})] | "
"//button[normalize-space(.) = {0}] |"
"//button[@data-original-title = {0}] |"
"//a[@data-original-title = {0}]/.. |"
"//a[@title = {0}]/.. |"
"//button[@title = {0}]")
.format(xpath_quote(root)))
[docs]def sub_loc(sub):
""" Returns the locator of the sub button
Args:
sub: The string name of the button.
Returns: A locator for the sub button.
"""
return (
By.XPATH,
("//div[contains(@class, 'btn_sel_text')][normalize-space(text()) = {0}]/../.. |"
"//ul[contains(@class, 'dropdown-menu')]//li[normalize-space(.) = {0}]").format(
xpath_quote(sub)))
[docs]def select_n_move(el):
""" Clicks an element and then moves the mouse away
This is required because if the button is active and we clicked it, the CSS class
doesn't change until the mouse is moved away.
Args:
el: The element to click on.
Returns: None
"""
# .. if we don't move the "mouse" the button stays active
sel.click(el)
sel.move_to_element(".navbar-brand")
[docs]def select(*args, **kwargs):
logger.debug('Selecting %r', args)
if version.current_version() > '5.5.0.7':
return pf_select(*args, **kwargs)
else:
return old_select(*args, **kwargs)
[docs]def pf_select(root, sub=None, invokes_alert=False):
""" Clicks on a button by calling the click event with the jquery trigger.
Args:
root: The root button's name as a string.
sub: The sub button's name as a string. (optional)
invokes_alert: If ``True``, then the behaviour is little bit different. After the last
click, no ajax wait and no move away is done to be able to operate the alert that
appears after click afterwards. Defaults to ``False``.
Returns: ``True`` if everything went smoothly
Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
"""
sel.wait_for_ajax()
if isinstance(root, dict):
root = version.pick(root)
if isinstance(sub, dict):
sub = version.pick(sub)
if sub:
q_sub = xpath_quote(sub).replace("'", "\\'")
sel.execute_script(
"return $('a:contains({})').trigger('click')".format(q_sub))
else:
q_root = xpath_quote(root).replace("'", "\\'")
try:
sel.element("//button[@data-original-title = {0}] | "
"//a[@data-original-title = {0}]".format(q_root))
sel.execute_script(
"return $('*[data-original-title={}]').trigger('click')".format(q_root))
except sel.NoSuchElementException:
try:
sel.element("//button[@title={}]".format(q_root))
sel.execute_script(
"return $('button[title={}]').trigger('click')".format(q_root))
except sel.NoSuchElementException:
try:
sel.element("//button[contains(@title, {})]".format(q_root))
sel.execute_script(
"return $('button:contains({})').trigger('click')".format(q_root))
except sel.NoSuchElementException:
# The view selection buttons?
sel.click("//li/a[@title={}]/*[self::i or self::img]/../..".format(q_root))
if not invokes_alert:
sel.wait_for_ajax()
return True
[docs]def old_select(root, sub=None, invokes_alert=False):
""" Clicks on a button by calling the dhtmlx toolbar callEvent.
Args:
root: The root button's name as a string.
sub: The sub button's name as a string. (optional)
invokes_alert: If ``True``, then the behaviour is little bit different. After the last
click, no ajax wait and no move away is done to be able to operate the alert that
appears after click afterwards. Defaults to ``False``.
Returns: ``True`` if everything went smoothly
Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
"""
# wait for ajax on select to prevent pickup up a toolbar button in the middle of a page change
sel.wait_for_ajax()
if isinstance(root, dict):
root = version.pick(root)
if sub is not None and isinstance(sub, dict):
sub = version.pick(sub)
root_obj = 'ManageIQ.toolbars'
if sub:
search = sub_loc(sub)
else:
search = root_loc(root)
eles = sel.elements(search)
for ele in eles:
idd = sel.get_attribute(ele, 'idd')
if idd:
break
else:
raise ToolbarOptionGreyedOrUnavailable(
"Toolbar button {}/{} is greyed or unavailable!".format(root, sub))
buttons = sel.execute_script('return {}'.format(root_obj))
tb_name = None
for tb_key, tb_obj in buttons.iteritems():
for btn_key, btn_obj in tb_obj['buttons'].iteritems():
if btn_obj['name'] == idd:
tb_name = tb_key
if not tb_name:
raise ToolbarOptionGreyedOrUnavailable(
"Toolbar button {}/{} is greyed or unavailable!".format(root, sub))
sel.execute_script(
"{}['{}']['obj'].callEvent('onClick', ['{}'])".format(root_obj, tb_name, idd))
if not invokes_alert:
sel.wait_for_ajax()
return True
[docs]def is_active(root):
""" Checks if a button is currently depressed
Args:
root: The root button's name as a string.
Returns: ``True`` if the button is depressed, ``False`` if not.
"""
el = sel.element(root_loc(root))
class_att = sel.get_attribute(el, 'class').split(" ")
if {"pres", "active", "pres_dis"}.intersection(set(class_att)):
return True
else:
return False
[docs]def is_greyed(root, sub=None):
""" Checks if a button is greyed out.
Args:
root: The root button's name as a string.
Returns: ``True`` if the button is greyed, ``False`` if not.
"""
if sub:
btn = sub_loc(sub)
else:
btn = root_loc(root)
el = sel.element(btn)
class_att = sel.get_attribute(el, 'class').split(" ")
if sub:
if {"tr_btn_disabled", "disabled"}.intersection(set(class_att)):
logger.debug("%s option greyed out, mouseover reason: %s",
sub, sel.get_attribute(el, 'title'))
return True
else:
if {"disabled", "dis"}.intersection(set(class_att)):
return True
return False
RELOAD_LOC = (
".//div[@title='Reload current display']|"
".//button[@title='Reload Current Display' or "
"@title='Reload current display' or "
"@id='miq_request_reload']"
)
[docs]def refresh():
"""Refreshes page, attempts to use cfme refresh button otherwise falls back to browser refresh.
"""
if sel.is_displayed(RELOAD_LOC):
sel.click(RELOAD_LOC)
else:
sel.refresh()
[docs]def exists(root, sub=None, and_is_not_greyed=False):
""" Checks presence and usability of toolbar buttons.
By default it checks whether the button is available, not caring whether it is greyed or not.
You can optionally enable check for greyedness.
Args:
root: Button name.
sub: Item name (optional)
and_is_not_greyed: Check if the button is available to click.
"""
sel.wait_for_ajax()
if isinstance(root, dict):
root = version.pick(root)
if isinstance(sub, dict):
sub = version.pick(sub)
try:
greyed = is_greyed(root, sub)
if and_is_not_greyed:
return not greyed
else:
return True
except sel.NoSuchElementException:
return False