1

I am using the below code with the expectation that it would locate the visible button

element = @browser.button(text: 'Search', visible: true)

And it's not locating the visible button. But the below code locating the visible button

element = @browser.element(xpath: "//button[text()='Search']", visible: true)

Is this expected? Or is it an issue?

HTML Code

<button type="button" title="" data-uid="DSDL_U1" data-action="search" data-viewitemid="DSDL_U1_-1_L21" triggeredactionhandler="search" buttonprocesstarget="CUSTOMER_IND_QUICK_SEARCH_BY_ID_NAME" class="icon icon-detail-search" id="DSDL_U1_-1_L21_L_1_C" tabindex="10047">Search</button>

@justin

To answer your question

@browser.buttons(text: 'Search').count

return 6

@browser.buttons(text: 'Search', visible: true).count

return 2

Now I checked

@browser.buttons(text: 'Search', visible: true)[0].present?

returns true

but If I click, then it returns

element click intercepted: Element <button tabindex="5" type="button" class="icon icon-detail-url" id="U1_-1_L5_L_1_C" style="outline: orangered solid 2px !important; background-color: rgb(255, 255, 255);">...</button> is not clickable at point (43, 258). Other element would receive the click: <div id="homePageSearch" class="searchGrpContainer homePageSearch populated" data-renderer="menu" data-headertype="button" data-style="tree" data-zone="overlay">...</div>
  (Session info: chrome=89.0.4389.90)

But If write

@browser.buttons(text: 'Search',visible: true)[1].present?

this returns true but If triggers the click, then it clicks.

You asked me the html of the two elements

@browser.buttons(text: 'Search',visible: true)[0].html

returns

<button tabindex="5" type="button" class="icon icon-detail-url" id="U1_-1_L5_L_1_C" style="outline: orangered solid 2px !important; background-color: rgb(255, 255, 255);"><span>Search</span></button>

Second element

@browser.buttons(text: 'Search',visible: true)[1].html

returns

<button type="button" title="" data-uid="DSDL_U1" data-action="search" data-viewitemid="DSDL_U1_-1_L21" triggeredactionhandler="search" buttonprocesstarget="CUSTOMER_IND_QSEARCH_PARTYID_ALTCUSTID" class="icon icon-detail-search" id="DSDL_U1_-1_L21_L_5_C" tabindex="10047">Search</button>

When I click the first search button, it opens another window inside the same html, In that new window another search button is there, So it's matching the first search button(which I clicked to open) and the second search button is in the new window which I about to click. But program detects two search buttons as visible.

Rajagopalan
  • 5,465
  • 2
  • 11
  • 29
  • What is the html? Is the element an actual button tag or input type button? – titusfortner Mar 16 '21 at 14:27
  • @titusfortner I have updated the HTML element in the question. – Rajagopalan Mar 16 '21 at 14:35
  • 1
    Using text locator with element will find the top most matching text node. Check the tag name of the one you find, it likely is a parent element. – titusfortner Mar 16 '21 at 14:39
  • @titusfortner I have shown you the element which I want to click. It's having a button tag and text search and I gave visible parameter, but it's not locating the visible element and its locating the first element. – Rajagopalan Mar 16 '21 at 14:49
  • The locator you provided works for the html element you provided if the locator variable is 'Search' and the driver determines that the button element is displayed on the page. It's impossible to know why it wouldn't find the element without seeing the whole page. You might try the `:visible_text` locator (http://watir.com/watir-6-10/#locating-elements-by-text) instead of using multiple locators. You can also see exactly what XPath is getting used by doing: `Watir.logger.level = :info`. – titusfortner Mar 16 '21 at 15:32
  • @titusfortner I have updated the question with more detail, Can you check it now? I have put the actual locator now. – Rajagopalan Mar 16 '21 at 16:38
  • For the `element = @browser.button(text: 'Search', visible: true)`, what button is being matched? Can you get the `element.text`, `element.html` and `element.present?`? – Justin Ko Mar 17 '21 at 09:22
  • 1
    @JustinKo I have updated the question with full details. – Rajagopalan Mar 18 '21 at 18:08

1 Answers1

1

You ultimately have 2 issues:

  • How XPath works for text
  • How Selenium determines what is visible/displayed

How XPath works for text

The difference in text locating effectively comes down to the difference in XPath for dot vs text(). You can see an explanation in other questions - eg XPath: difference between dot and text(). Simply:

  • Dot: matches the concatenation of all direct and descendant text nodes
  • text(): matches only individual direct text nodes of the element

For the Watir locators:

@browser.button(text: 'Search', visible: true)                      # this uses dot
@browser.element(xpath: "//button[text()='Search']", visible: true) # this uses text()

For <button><span>Search</span></button> it has the concatenated text of "Search" but only the direct text nodes of "" and "". This is why Watir locates it using the :text locator, but not when using the :xpath locator with text().

In contrast, <button>Search</search> has the concatenated text of "Search" and direct text node of "Search". As a result, it will be matched by both Watir locators.

How Selenium determines what is visible/displayed

Selenium considers elements that are covered by another element as being displayed, which means that it is considered present by Watir. For example, an element under a dialog's overlay would still be considered visible to the user. Both of your buttons are considered visible.

There isn't a Watir locator that can differentiate elements that are covered (obscured) by other elements. However, you could manually do it:

@browser.buttons(text: 'Search', visible: true).find { |btn| !btn.obscured? }

Solution

Given the HTML of the 2 buttons, I think the best approach is to actually locate the button using a different attribute. "triggeredactionhandler" appears to be unique:

@browser.button(triggeredactionhandler: 'search')
Justin Ko
  • 46,526
  • 5
  • 91
  • 101