Repository: ManageIQ/integration_tests

Issue:
[WIP] - Button fix
@mfalesni this is an attempt to fix the buttons for credential validation for multiple credentials. In 5.5 when you change the credential tab, the FormButton fails to find the second button as it is finding the first. This is due to some madness with button and a elements hidden unhidden.....you name it.

So I started looking at it, fixed the "locate" but the "dimmed" is much more difficult because it relies on fixing some of hte previous xpath as well.....I'm losing my patience with it, hoping maybe you can fix it up.



Discussion:

Resolving patch:
diff --git a/cfme/web_ui/form_buttons.py b/cfme/web_ui/form_buttons.py
--- a/cfme/web_ui/form_buttons.py
+++ b/cfme/web_ui/form_buttons.py
@@ -23,6 +23,20 @@ class FormButton(Pretty):
     """
     pretty_attrs = ['_alt', '_dimmed_alt', '_force', '_partial']
 
+    class Button:
+        """Holds pieces of the XPath to be assembled."""
+        TAG_TYPES = "//a | //button | //img | //input"
+        TYPE_CONDITION = (
+            "(contains(@class, 'button') or contains(@class, 'btn') or contains(@src, 'button'))"
+        )
+        DIMMED = "(contains(@class, 'dimmed') or contains(@class, 'disabled'))"
+        NOT_DIMMED = "not{}".format(DIMMED)
+        IS_DISPLAYED = (
+            "not(ancestor::*[contains(@style, 'display:none') "
+            "or contains(@style, 'display: none')])")
+        ON_CURRENT_TAB = (
+            "not(ancestor::div[contains(@class, 'tab-pane') and not(contains(@class, 'active'))])")
+
     def __init__(self, alt, dimmed_alt=None, force_click=False, partial_alt=False):
         self._alt = alt
         self._dimmed_alt = dimmed_alt
@@ -37,34 +51,39 @@ def alt_expr(self, dimmed=False):
             return "(normalize-space(@alt)={})".format(
                 quoteattr((self._dimmed_alt or self._alt) if dimmed else self._alt))
 
+    def _format_generator(self, dimmed=False, include_dimmed_alt=False):
+        """Generates a dict that will be passed to the formatting strings."""
+        d = {}
+        for key, value in self.Button.__dict__.iteritems():
+            if not key.startswith("_"):
+                d[key] = value
+        d["ALT_EXPR"] = self.alt_expr(dimmed=dimmed)
+        if include_dimmed_alt:
+            d["DIMMED_ALT"] = quoteattr(self._dimmed_alt or self._alt)
+        return d
+
     def locate(self):
-        """This hairy locator ensures that the button is not dimmed and not hidden."""
-        return ("(//a | //button | //img | //input)[{} and not(contains(@class, 'dimmed'))"
-                " and (contains(@class, 'button') or contains(@class, 'btn')"
-                " or contains(@src, 'button'))"
-                " and not(ancestor::*[contains(@style, 'display:none')"
-                " or contains(@style, 'display: none')])]".format(self.alt_expr(dimmed=False)))
+        return (
+            "({TAG_TYPES})[{ALT_EXPR} and {NOT_DIMMED} and {TYPE_CONDITION} and {IS_DISPLAYED} "
+            "and {ON_CURRENT_TAB}]"
+            .format(**self._format_generator(dimmed=False)))
 
     @property
     def is_dimmed(self):
-        return sel.is_displayed("(//a | //button | //img | //input)[{}"
-            " and contains(@class, 'dimmed')"
-            " and (contains(@class, 'button') or contains(@class, 'btn')"
-            " or contains(@src, 'button'))"
-            " and not(ancestor::*[contains(@style, 'display:none')"
-            " or contains(@style, 'display: none')])]|//button[normalize-space(.)='{}' and"
-            " (@disabled='true' or contains(@class, 'btn-disabled'))"
-            " and not(ancestor::*[contains(@style, 'display:none')"
-            " or contains(@style, 'display: none')])]".format(
-                self.alt_expr(dimmed=True), self._dimmed_alt or self._alt
-            ))
+        locator = (
+            "({TAG_TYPES})[{ALT_EXPR} and {DIMMED} and {TYPE_CONDITION} and {IS_DISPLAYED} "
+            "and {ON_CURRENT_TAB}]"
+            "|"  # A bit different type of a button
+            "({TAG_TYPES})[normalize-space(.)={DIMMED_ALT} and {IS_DISPLAYED} and "
+            "(@disabled='true' or contains(@class, 'btn-disabled')) and {ON_CURRENT_TAB}]"
+            .format(**self._format_generator(dimmed=True, include_dimmed_alt=True)))
+        return sel.is_displayed(locator)
 
     @property
     def can_be_clicked(self):
         """Whether the button is displayed, therefore clickable."""
         try:
-            sel.move_to_element(self)
-            return sel.is_displayed(self)
+            return sel.is_displayed(self, move_to=True)
         except NoSuchElementException:
             return False
 
@@ -76,7 +95,7 @@ def _custom_click_handler(self):
         """Handler called from pytest_selenium"""
         sel.wait_for_ajax()
         if self.is_dimmed and not self._force:
-            logger.info("Not clicking {} because it is dimmed".format(str(repr(self))))
+            logger.info("Not clicking {} because it is dimmed".format(repr(self)))
             return
         sel.wait_for_element(self, timeout=5)
         return sel.click(self, no_custom_handler=True)


Test patch: