Selenium FirefoxDriver clicks on wrong link when animations occur - java

I am using Selenium 2.35 and having a unpredictable error while trying to click on an element in firefox like this:
new Actions(driver).moveToElement(element).click().perform();
The element I've located is a < span > element with a click event tied to it. The issue I am running into is occasionally when Firefox tries to click on the element is misses and clicks on an entirely different element. If I walk through my code using the debugger the issue never occurs which leads me to believe that the FirefoxDriver is just clicking on the wrong location on the browser, having to do with a timing issue. There are dynamically loaded < div >'s on my page that make the element I want to click shift down just before I locate it and send the .click() command. I believe this is the source of my issue. I can put a Thread.sleep(500) is ensure that it has enough time to finish animating and inserting the dynamic divs, but that seems sloppy to me. Is there a way I can tell my FirefoxDriver to wait until the elements on the screen are done shifting around before it tries to send the .click() event?
Also, I have tried to send just the element.click() but that seems to fail more often.
Update (9/5/13):
The solution I came to with the help of #MrTi 's comment was the following:
private void jsClickOnElementById(String id)
{
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id(id)));
JavascriptExecutor js = (JavascriptExecutor)driver;
StringBuilder sb = new StringBuilder();
sb.append("var x = $('#" + id + "');");
sb.append("x.click();");
js.executeScript(sb.toString());
}
Just to ensure the element is actually on the page I added the initial "wait" call, and then use the JavascriptExecutor to run some jquery and click on the element on the page. This seems to work great for my case. If anyone has help as to some drawbacks to this solution they would be greatly appreciated as I'm fairly new to the JavascriptExecutor class.

In addition to ensuring that the page is actually visible with ExpectedConditions, this situation could be solved by handling animations (transitions) on page.
It's possible to disable all the animations on page, until page is refreshed, by executing:
(JavascriptExecutor)driver.executeScript("$('body').append('<style> * {transition: none!important;}</style>')")
Also, If you know the specific element that got the animation you can check if it's animated (JQuery animation selector):
(Boolean)((JavascriptExecutor)driver).executeScript("return $("#ELEMENT_ID").is(":animated")")
If the specific element unknown you can determine if all animations have finished:
(Boolean)((JavascriptExecutor)driver).executeScript("return $(":animated").length == 0 ")

Sadly in 2018 (five years later) I can confirm element.click() in selenium doesn't always click the right element. I still have to shell-out to jquery with JavascriptExecutor to click on an element by ID via jquery $(selector).click() to hit the element with confidence.
And it's NOT just that it can't find the element due to scrolls, animation, or offscreen, it appears Selenium's wrapper of Marionette's internal DOM tree is out of sync with firefox's geckodriver somehow for dynamically loaded elements (in this case a table row)

I believe what is happening is that the dynamic loading is making your selector select something else. If you post your HTML, (especially the before/after the dynamic loading) that would be immensely helpful in writing a better selector.
However, there are a couple of ways to wait until the loading is finished.
The first to wait until (Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0") returns true. This waits for all JQuery on the page to finish (which is probably what is causing the dynamic loading). Its useful, but I would recomment waiting for something else.
The second option is to wait until an element that is dynamically loaded is present:
wait.until(ExpectedConditions.presenceOfElementSelectedBy(...selector...));
I like this method, as it will wait exactly as long as you need to wait, and no longer.
The final option is to write a selector that will always select the element, even if other stuff is going on. This may be your best option, but you may also run into StaleElementReferenceExceptions.

Related

Element could not be scrolled into view (even though it is right there) Selenium

I am using Selenium to run tests on a page with multiple drop-down menus (specifically a pop-up page which allows you to select some options then close it). I am able to click on some of these menus totally fine; however, some of them throw an ElementNotInteractable exception with the message "element could not be scrolled into view", even though the menus are right beside each other. I am very confused as to why one menu works and the other does not even though they appear to be the same. The three things I have tried in order to click on the menu are:
a) Regular Selenium clicking :
driver.findElement(By.xpath("//select[#foo='bar']").click();
This is what works with the other menus, except I navigate directly to the "option" tag and click on it (don't need to click the drop down first)
b)Javascript executor
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", element);
When I use this, no exceptions are thrown; however, the menu remains empty, which leads me to believe it is not being clicked on.
c)Actions
Actions builder = new Actions(driver);
builder.moveToElement(menu).click(menu);
builder.perform();
For some reason when I use this, the whole pop-up window with the drop down menus on it closes. :/ (I have double checked that it is not the close button being clicked)
I'm not sure if this is relevant, but Selenium has no problem finding the elements, it is just when I try to click them that it complains.
To summarise, my questions are:
1) What could make the menus different such that one is clickable and one is not?
2) How can I click on the second menu and choose an option?
Edit: I tried the solution found in the similar problem; unfortunately it does not work. The solution was to add an explicit wait since the element may not have completely loaded, this only leads to a timeout.
Using JavascriptExecutor is a workaround to interact with non interactable elements. I think it should never be used in selenium tests because it makes the test do things a real user wouldn't be able to do in a real life scenario.
The most plausible cause is that you are interacting with the wrong element, try to debug to identify the element returned by the used selector.
You can use chrome dev tools in debug mode :
1- Put a breakpoint at the exception line,
2- Use $x("//select[#foo='bar']") in the chrome console to get the element.
To select a value, you can use the org.openqa.selenium.support.ui.Select object :
new Select(element).selectByValue(value);

Selenium/Java: Drag and drop operation not working for invisible elements

Using Selenium w/ Java bindings and ChromeDriver 2.3 with the latest browser installed
I'm currently stumped after more than enough time trying to find a solution. I'm currently trying to drag and drop an element to another element. The only catch is the element target I need to move to is only visible once I move the source element. Any advice?
So here a screenshot of the source element that I click and hold, and then dragging it exposes the two options that I can drop too:
Element that is the source
So as I hold down the mouse and drag just a small portion, the drop targets become visible, per the screenshot below:
Targets visible once mouse is dragged with source
Here is the latest code snippet I've tried that I believe should make this happen, but yet nothing happens and continues into the Thread.sleep(), which was only put in for observation purposes. These are assuming healthy instances of WebDriverWait and WebDriver:
driverWait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#class='user-info ng-binding'][text()='Sample Text']")));
driverWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[#class='user-info ng-binding'][text()='Sample Text']")));
source = driver.findElement(By.xpath("//div[#class='user-info ng-binding'][text()='Sample Text']"));
//move to element, click and hold, and then move it to expose the available options
actions.moveToElement(source).clickAndHold().pause(Duration.ofSeconds(1)).moveByOffset(10, 10).pause(Duration.ofSeconds(1)).build().perform();
Thread.sleep(10000);
So the idea behind this is to just click and hold the source, move it a little to display the targets, and then find the targets, and use the actions.release() to move the target onto the source, but when I debug it it does nothing on the element to the element and goes straight to the Thread.sleep(). I've read about plenty of bugs. Any advice would be greatly appreciated.
I know this is an old question but I was looking for a solution but in the end I just made the hidden area visible. I know this isn't ideal but seems to work.
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].setAttribute('style', 'display:block')", targetElement);

Selenium Webdriver (Java) , Need to send "Space" keypress to the website as whole

My problem is as follows.
I am attempting to automate part of a test suite for a website I work in, and while most of it went well, I'm at the point where the developers added a lightbox to confirm the next action.
Using firebug I found out an xpath I could use to click the button I need to proceed, but sadly it isn't working.
After some manual attempts, I figured that pressing the "Space" key, I can proceed.
The problem is that any sort of try using "driver.findElement" be it by xpath, or link text, fails with a "No such element" error in console.
So I'm trying to just send the keypress of Space , without using find element.
To be clear, I want to emulate someone just pressing space without clicking or selecting anything beforehand.
I tried with driver.Keyboard... but "Keyboard" isn't recognized, so I'm at a loss of how to send this keypress without using driver.findElement.
The piece of code giving me problems is:
driver.findElement(By.xpath("//div[4]/div[3]/div/button")).click();
Any help would be appreciated.
Thank you and have a great day!
If you receive NoSuchElementException, but you know that the element is there, then it seems that the element get loaded into the DOM later (with ajax?), than the page get loaded.
In this case you should use Implicit or Explicit Wait to wait until an element present, or an element is visible, etc...
If it still doesn't work, and you want to try the Space Key thing, then you can perform it on any element, for example on the <body> tag:
WebElement body = driver.findElement(By.tagName("body"));
body.sendKeys(Keys.SPACE);
Hope it helps.
try this:
Actions action = new Actions(Driver);
action.SendKeys(Keys.Space).Build().Perform();

Selenium: Clicking on toaster issues

I'm trying to click on an element on a page; the element is clearly visible on screen. There is a toaster that might pop up, so I'm trying to write a defense: if the toaster is on screen, close the toaster first, then continue clicking through to the next page. I am using PageFactory, so I have an element to contain the toaster and one for the close button for the toaster. My "deal with toaster" method is as follows:
if (driver.findElements(By.cssSelector("#toaster")).size() > 0
&& toaster.isDisplayed()) {
toasterClose.click();
}
When I do this in chrome, however, I'm getting org.openqa.selenium.WebDriverException: unknown error: Element is not clickable at point (994, 758)
Pausing the test execution, I cannot see the toaster on the screen. I figure the devs must be hiding it by making it render in a far away, unscrollable location. So as a stopgap measure, I added a condition that if the x coordinate was greater than 800, don't click. With that in place, I get:
org.openqa.selenium.WebDriverException: unknown error: Element is not clickable at point (547, 725). Other element would receive the click: <div id="toaster">...</div>
What's going on? How can the toaster not be clickable but would somehow receive the click anyway? Firefox can handle the test just fine, with or without the 800 pixel workaround; it's only Chrome having this issue.
For Clarification: The goal of the test is NOT to click the toaster. The goal is to click another element on the page. The test reported that a toaster was in the way, so I attempted to write a step to close the toaster if it is displayed. I have not seen this toaster, so I'm not exactly sure what it is, but chrome keeps reporting that it's in the way. All our toasters site-wide use a basic template that includes a close button so the user can close the toaster, which is what I'm trying to click. Firefox never has this issue and does not report the existence of any toasters.
I'm calling it a toaster because that's what our site calls it, because that's what it's called in whatever framework we got it from (jQuery UI? Backbone?). If I pause execution, I cannot see any toasters at this point in the test, but jQuery tells me it exists and is visible. However, the element found with jQuery has just the default pieces of our toaster setup: a div, an empty div where the message should be, and the close button. Clearly it's not meant to be rendered at this time, but Chrome thinks it's in the way.
I'm assuming by "toaster" you mean some sort of javascript modal popup with a close button.
Identifying the correct problem
You're testing the existence and visibility of the #toaster element, but not the toasterClose element that you're clicking. There's no guarantee that just because one element exists and is displayed, another is as well. From the error, it appears that the #toaster element overlaps the toasterClose element, making it unclickable.
Troubleshooting clickability
Once you've properly selected toasterClose, manually use devtools and inspect to see why it's unclickable. Is it visible and unobstructed? Is the toasterClose element something of zero height/width? Is there dynamic JavaScript modifying the page post-load? Is it actually positioned in view of the page? (I've had elements render visibly at the edge of the window only to be obstructed by the browser's scroll bars.1)
Alternative
You should also see if you really need to use this toasterClose element. How does would a human close this popup? Would they press Escape? Would they click outside the popup window, on the overlay element? Do they do something else that triggers some sort of closeModal() javascript function? You can also do any of these things using Selenium.
Last Resort
One thing you can always do to remove such a popup is to run your own javascript to modify the DOM and remove the offending element(s) altogether:
driver.execute_script(<<-javascript)
var toaster = document.getElementById("toaster");
toaster.parentNode.removeChild(toaster);
var overlay = document.getElementById("modal_overlay");
overlay.parentNode.removeChild(overlay);
javascript
Future/Additional
If this is a regular issue for you, I would suggest wrapping this code in try/catches and a retry mechanism to make it resilient to javascript dynamically loaded elements.
1 Update
Just to elaborate on the scrollbar issue I had, because it turned out that it was a very similar problem to yours.
Here, the "I'm Feeling Lucky" button is out of view. If Selenium tries to click on it, first it will attempt to scroll it into view.
Here's an example of what Selenium would attempt to do. Notice how the button is now "in view".
However, Chrome on OSX is styled in such a way that the scrollbars are normally hidden. The moment that Selenium issues the scroll command, the scrollbars appear and the following click command fails to reach the button.
The solution was to use javascript to scroll the window manually:
page.execute_script(<<-javascript)
document.getElementById("gbqfsb").scrollIntoView(true);
// or if that doesn't work:
window.scrollTo(0, document.getElementById("gbqfsb").getBoundingClientRect().top);
javascript
Try the following code. Should work:
if (driver.findElements(By.cssSelector("#toaster")).size() > 0
&& toaster.isDisplayed()) {
Actions builder = new Actions(driver);
builder.moveToElement(toasterClose).moveByOffset(2,2).click().build().perform();
}
Can you close toaster using escape key from keyboard manually.
If you can than use following:
Actions action = new Actions(driver);
action.sendKeys(Keys.ESCAPE).build().perform();
It seems the toaster was partially rendered and fixed to the DOM just below the bottom edge of the screen, using position:fixed to stop it from showing up until it's ready to be populated with data and animated onto the screen. When chrome tried to click on links that were below the bottom edge of the screen, it predicted that it'd hit the toaster and didn't actually bother scrolling.
After some googling, I added the following utility function:
public static void ScrollElementIntoView(WebDriver driver, WebElement element) {
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
}
Then I call this method before clicking any link on that page, and voila, no more toaster problems!

Java selenium. disable auto scrolling of the page

I'm using selenium of Java for automation tests and the browser is Firefox.
This is my sample code:
WebElement elem = driver.findElement(By.xpath(".//*[#id='main']/div/div[3]/div[1]/div/div[3]/div/div/div/a"));
Actions action = new Actions(driver);
Actions action2 = action.moveToElement(elem);
action2.perform();
The problem is that "moveToElement" action is triggering auto scrolling event of the page.
I want the page to remain as it was before without the scrolling.
somebody may know how can I disable this auto scrolling?
Thanks.
You can't. WebDriver scrolls elements into view when acting on them.
You can't disable the autoscroll.
Some possible workarounds:
You could try to fire a syntetic mouseover event over your WebElement if that would help your cause.
After the moveToElement(), you could try rescroll to your needed position with window.scrollTo() or for example the Page Up key. This would, obviously, break the mouseover on the element, but maybe it's what you need.
You can position your real mouse cursor over the element via the Robot class. This might get a little tricky, you might need to enter the fullscreen mode with your browser (or use this) and then handle the scroll offsets manually, if there are some.
All depends on your intentions, on what you really need to do with the element and why.

Categories

Resources