Doggo
Doggo

Reputation: 95

What would prevent capybara/selenium from hovering over a visible element?

I am writing automation tests for a webpage. I can't share any specific details, so all I'm looking for is some general brain-storming to help me figure out what is causing the problem. A long-shot, I know, but I've become obsessed with this problem.

There is an element with id="troublesome" on the webpage. On manual testing, hovering over #troublesome will cause it to disappear and something else pops up in its place (as it should). I'm trying to verify that the pop-up occurs on hover using automation testing (Capybara, selenium driver, ruby). However, no matter what technique I use, hovering doesn't work.

troublesome is visible upon visiting the page. It is not cut off by screen size. Capybara has no trouble finding it and reading its text and attributes. i.e. A regular ol' find("#troublesome").text will return the correct text.

However, I cannot use Capybara to click on #troublesome without executing javascript.

i.e. find("#troublesome").click won't do anything (it won't throw any errors either). I must use find("#troublesome").execute_script("this.click()") to click on it.

But I don't need to click it. I just need to hover over it.

Using Capybara:

find("#troublesome").hover --> will not work. No errors thrown either. Test just continues until it fails because it fails to find the expected result. Telling ruby to sleep(however_many_seconds) doesn't help.

Using Selenium:

page.driver.browser.action.move_to(find("#troublesome").native).perform --> this doesn't work either. Again, no errors thrown.

Using trigger:

find("#troublesome").trigger(:mouseover) --> doesn't work because selenium driver doesn't support trigger (and I don't want to use another driver).

Using jquery:

Won't work. Website doesn't use jquery

Attempting to use javascript to force :hover to be true on element doesn't work:

mouseHover = 'var x = document.createEvent("MouseEvent"); x.initMouseEvent("mouseover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); document.getElementById("troublesome").dispatchEvent(x);'

page.execute_script(mouseHover)

(I can change mouseOver to click, and it'll click though!) Apparently, mouseover is not 'trusted' by browser (but click is), so kinda useless to have that as an option, isn't it?

I've tried all of the above by working within a within("#id") do... end block. Doesn't make a difference.

I've even tried unconventional means to get the mouse over #troublesome:

find("#troublesome").right_click --> the mouse will be DIRECTLY over #troublesome, right-click, and a menu will pop up RIGHT OVER the element!!!!!!

So CLEARLY, the mouse IS hovering over #troublesome during my automation test, yet it's not registering on the browser. The website is not bugged. Hovering works when I do it manually.

I can find other elements on the webpage and hover over them just fine. In fact, I've even tried putting the mouse over another element, then moving it from there to #troublesome like so:

page.driver.browser.action.move_to(find("#somethingElse").native, 1200, -50).perform

That doesn't work, but if I adjust the coordinates to a third element just below #somethingElse, this will trigger the third element's hover state, so clearly this strategy can work in principle and practice, yet not for #troublesome!

Note that #somethingElse and the 'third element' exist on a div that is at the same 'heirarchy' as the ancestor div of #troublesome.

There are iframes on the webpage, but #troublesome is not on the iframe.

There are random script tags inserted all over the body of the webpage. I don't know what those script tags are doing as I can't see the code.

#troublesome has become my Moby Dick.

This whale is driving me nuts. I've invested too much time into it already. I can't give up now or all those hours of toil would be for nothing.

Please help.

Thank you.

find("#troublesome").hover

page.driver.browser.action.move_to(find("#troublesome").native).perform

find("#troublesome").trigger(:mouseover)

mouseHover = 'var x = document.createEvent("MouseEvent"); x.initMouseEvent("mouseover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); document.getElementById("myDiv").dispatchEvent(x);'

page.execute_script(mouseHover)

page.driver.browser.action.move_to(find("#somethingElse").native, 1200, -50).perform

No error messages other than standard capybara/rspec failure log because it failed to find the element that was supposed to pop up upon hovering over #troublesome

Upvotes: 3

Views: 1142

Answers (2)

Thomas Walpole
Thomas Walpole

Reputation: 49910

The problem you're running into is because the '.hoverme' element in your example at https://jsfiddle.net/pwo7zuL2/1/ has a size of 0x0 px (this is also why you couldn't click it). The size is 0x0 because it only contains absolute positioned elements which don't technically count when calculating the auto size of the parent. If instead of attempting to hover over the .hoverme element you hover over the visible absolute positioned child (which actually has size) of the element the hover will work correctly (which is what you are actually doing when you do it manually in your example).

find('#next').hover

Upvotes: 1

Doggo
Doggo

Reputation: 95

I now know the problem, and I have a solution (albeit a hacky one).

The problem is due to an iframe which covers the full screen. This blocks selenium from being able to hover over #troublesome even though #troublesome is not within the iframe and #troublesome's z-index is set to a high number (thereby forcing it to be at the top layer).

Manual hovering works, but hovering with selenium fails. I believe this is a bug, so I have reported it on selenium's github.

One solution that works is to use javascript to force the iframe to shrink, then hover (which should work now), then use javascript to return the iframe to full screen (so as to not affect other aspects of the test).

shrink_frame = 'document.getElementById("#frame").setAttribute("style", "width: 100px; height 100px");'

page.execute_script(shrink_frame)

This is not an ideal solution because it is obviously not how a real user would interact with the webpage, but it's acceptable given that this is due to a selenium bug and that manual testing works.

Upvotes: 0

Related Questions