Performance issue with Headless Chrome [duplicate] - java

This question already has answers here:
Selenium Web Driver & Java. Element is not clickable at point (x, y). Other element would receive the click
(9 answers)
Closed 5 years ago.
My scripts are failing only when the headless mode is enabled. Please suggest me the ways what all I should look while coding.
Most of my scripts are failed due to TimedOut.
Is there anything I should specifically look for locators? I believe css wont help me in this case. Would there be something else related to locators?
In the continuous integration server, the execution is very slow. The limitations of Timed_Out seconds are fixed to 50. This didn't work for me and even for 100. Please suggest me how to handle when I'm restricted to utilize only upto 100seconds to TimeOut and it meets exception as it requires more seconds than this.
Here are few exceptions that I receive only when I enable headless,
1. WebDriverException: unknown error: Element <input type="radio" class="wizard-input" name="5a68a4c173bb-TOTAL-1" id="5a68a4c173bb-TOTAL-1" value="1"> is not clickable at point (496, 551). Other element would receive the click: <div class="navigation-bar">...</div>
Tried to apply wait condition and even scrolled and click.
2. TimeoutException: Expected condition failed: waiting for element to be clickable: By.cssSelector: div.icon.icon-add.add-contact-button (tried for 50 second(s) with 500 MILLISECONDS interval)
Tried to apply the conditions suggested by Marcel. As told it exceeds even 100seconds
Here are few examples of my code,
public void clickForwardButton(){
WaitTillElementToBeClickable("xpath", LoginData.Forward);
ScrollAndClickOnElement("xpath", LoginData.Forward);
} //The error seems to be like it wont scroll properly and hence I receive element not found exception
protected void WaitTillElementToBeClickable(String locatorType, String locatorValue) {
try {
WebDriverWait wait = new WebDriverWait(driver, TIME_OUT_IN_SECONDS);
if (locatorType.equalsIgnoreCase("cssSelector")) {
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector(locatorValue)));
} else if (locatorType.equalsIgnoreCase("xpath")) {
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(locatorValue)));
} else if (locatorType.equalsIgnoreCase("id")) {
wait.until(ExpectedConditions.elementToBeClickable(By.id(locatorValue)));
}
} catch (Exception e) {
logger.error("Webdriver Locator Error" + e);
}
}

If you're not using WebDriverWait, give that a try
int seconds = 5;
WebDriverWait wait = new WebDriverWait(driver, seconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("yourID")));
You have to import OpenQA.Selenium.Support.UI to be able to use WebDriverWait.
Edit
Since the WebDriverWait method doesn't provide a solution, try adding an additional argument to your ChromeOptions to set the window size. Since the default headless window size is probably a lot smaller than your non-headless window size, it's worth a try. Additional benefit of setting a larger window size is reducing the need for scrolling.
ChromeOptions options = new ChromeOptions();
options.addArgument("headless");
options.addArgument("window-size=1920,1080");
// or
options.addArguments("headless", "window-size=1920,1080");

Related

Getting error for choosing radio button(Element is not clickable at point (286,871) because another element ) [duplicate]

I see this only in Chrome.
The full error message reads:
"org.openqa.selenium.WebDriverException: Element is not clickable at point (411, 675). Other element would receive the click: ..."
The element that 'would receive the click' is to the side of the element in question, not on top of it and not overlapping it, not moving around the page.
I have tried adding an offset, but that does not work either. The item is on the displayed window without any need for scrolling.
This is caused by following 3 types:
1.The element is not visible to click.
Use Actions or JavascriptExecutor for making it to click.
By Actions:
WebElement element = driver.findElement(By("element_path"));
Actions actions = new Actions(driver);
actions.moveToElement(element).click().perform();
By JavascriptExecutor:
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("scroll(250, 0)"); // if the element is on top.
jse.executeScript("scroll(0, 250)"); // if the element is on bottom.
or
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("arguments[0].scrollIntoView()", Webelement);
Then click on the element.
2.The page is getting refreshed before it is clicking the element.
For this, make the page to wait for few seconds.
3. The element is clickable but there is a spinner/overlay on top of it
The below code will wait until the overlay disppears
By loadingImage = By.id("loading image ID");
WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
wait.until(ExpectedConditions.invisibilityOfElementLocated(loadingImage));
Then click on the element.
You can also use JavaScript click and scrolling would be not required then.
IJavaScriptExecutor ex = (IJavaScriptExecutor)Driver;
ex.ExecuteScript("arguments[0].click();", elementToClick);
There seems to be a bug in chromedriver for that (the problem is that it's marked as won't fix)
--> GitHub Link
(place a bounty on FreedomSponsors perhaps?)
There's a workaround suggested at comment #27.
Maybe it'll work for you-
I had the same issue, tried all offered solutions but they did not work for me.
eventually I used this:
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("var evt = document.createEvent('MouseEvents');" + "evt.initMouseEvent('click',true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0,null);" + "arguments[0].dispatchEvent(evt);", findElement(element));
Hope this helps
Wow, a lot of answers here, and many good ones.
I hope I'll add something to this from my experience.
Well guys, in my case there was a cookie overlay hiding the element occasionally.
Scrolling to the element also works; but in my humble opinion (for my case, not a panacea for everyone) the simplest solution is just to go full screen (I was running my scripts on a 3/4 of the screen window)! So here we go:
driver.manage().window().maximize();
Hope that helps!
You need to use focus or scroll on that element. You also might have to use explict wait.
WebElement firstbutton= driver.findElement(By.xpath("Your Element"));
Actions actions = new Actions(driver);
actions.moveToElement(element);
actions.perform();
OR
The element is not clickable because of a Spinner/Overlay on top of it:
By loadingImage = By.id("loading image ID");
WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
wait.until(ExpectedConditions.invisibilityOfElementLocated(loadingImage));
OR
Point p= element.getLocation();
Actions actions = new Actions(driver);
actions.moveToElement(element).movebyoffset(p.x,p.y).click().perform();
OR
If still not work use JavascriptExecutor
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", firstbutton);
I have seen this in the situation when the selenium driven Chrome window was opened too small. The element to be clicked on was out of the viewport and therefore it was failing.
That sounds logical... real user would have to either resize the window or scroll so that it is possible to see the element and in fact click on it.
After instructing the selenium driver to set the window size appropriately this issues went away for me. The webdriver API is decribed here.
I was getting this error when running tests headless with xvfb-run. They were working flawlessly locally. Using chrome, versions of webdriver / chromedriver / chrome / java etc all identical.
The ‘won’t fix’ bug in chromedriver - GitHub Link pointed out by Tony Lâmpada suggested this may be related to what is / isn't visible on the screen.
Help message for xvfb-run shows the following:
-s ARGS --server-args=ARGS arguments (other than server number and
"-nolisten tcp") to pass to the Xvfb server
(default: "-screen 0 640x480x8")
Changing the resolution for xvfb made the error go away:
xvfb-run -s "-screen 0 1280x1024x16" ...
ruby/watir-webdriver/chrome
I use the following trick and seems like it works:
#scroll to myelement
#browser.execute_script "window.scrollTo(#{myelement.element.wd.location[0]},#{myelement.element.wd.location[1]})"
# click myelement
myelement.when_present.fire_event("click")
I, too, wrestled with this problem. Code works fine in FF, fails on Chrome. What I was trying to do was to click a tickbox - if it wasn't in view, I'd scroll to view and then click. Even scrolling into view works in Chrome, only the bottom few pixels of the tickbox wasn't visible so webdriver refused to click on it.
My workaround is this:
WebElement element = _sectorPopup.findElement(...);
((Locatable) element).getCoordinates().inViewPort();
try {
element.click();
} catch (Exception e) {
new Actions(getWebDriver()).sendKeys(Keys.PAGE_DOWN).perform();
element.click();
}
Chrome also has issues with sendKeys, using Actions is sometimes necessary. Obviously, you need to know which direction and how much you need to go so your mileage may vary. But I prefer this to the javascript hack, so I'm posting it here in case someone else will find it useful.
First, try to get the latest Chrome driver and check if it solves the issue.
In my case, it didn't fix the issue. But, the following solution worked for me so far. The following is C# code but you can follow same logic in your specific language. What we do here is,
Step 1: Focus on the element using the Selenium Actions object,
Step 2: Then do a click on the element
Step 3: If there's an exception, then we trigger a javascript "Click" event on the element by executing the javascript script through the Selenium browser driver's "ExecuteScript" method.
You can also skip step 1 and 2 and try only step 3 too. Step 3 would work on it's own but I noticed some strange behavior in one scenario in which step 3, even though it successfully clicked the element, caused unexpected behavior in other parts of my code after clicking the element.
try
{
//Setup the driver and navigate to the web page...
var driver = new ChromeDriver("folder path to the Chrome driver");
driver.Navigate().GoToUrl("UrlToThePage");
//Find the element...
var element = driver.FindElement(By.Id("elementHtmlId"));
//Step 1
new Actions(driver).MoveToElement(element).Perform();
//Step 2
element.Click();
}
catch (Exception)
{
//Step 3
driver.ExecuteScript("document.getElementById('elementHtmlId').click();");
}
I was getting the same issue while running selenium script in python. Here is what I used to click on the element:
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(driver).click(element).perform()
When using Protractor this helped me:
var elm = element(by.css('.your-css-class'));
browser.executeScript("arguments[0].scrollIntoView();", elm.getWebElement());
elm.click();
I made this method based on a comment from Tony Lâmpada's answer. It works very well.
def scroll_to(element)
page.execute_script("window.scrollTo(#{element.native.location.x}, #{element.native.location.y})")
end
Today I got the same kind of issue. You don't believe me if i say how i solved the issue.
By maximizing the browser size
Yes, it is a pointer issue that means the size of the browser. For that, you just need to maximize the window size manually or through the code.
I was facing a similar problem whre i have to check two check boxes one after the other.But i was getting the same above error.hence i added wait in between my steps for checking the checkboxes....its working fine and great.here are the steps:-
When I visit /administrator/user_profiles
And I press xpath link "//*[#id='1']"
Then I should see "Please wait for a moment..."
When I wait for 5 seconds
And I press xpath link "//*[#id='2']"
Then I should see "Please wait for a moment..."
When I visit /administrator/user_profiles_updates
Apparently this is the result of a "Won't Fix" bug in the Chrome driver binary.
One solution that worked for me (Our Mileage May Vary) can be found in this Google Group discussion, Comment #3:
https://groups.google.com/forum/?fromgroups=#!topic/selenium-developer-activity/DsZ5wFN52tc
The relevant portion is right here:
I've since worked around the issue by navigating directly to the href of
the parent anchor of the span.
driver.Navigate().GoToUrl(driver.FindElement(By.Id(embeddedSpanIdToClick)).FindElement(By.XPath("..")).GetAttribute("href"));
In my case, I'm using Python, so once I got the desired element, I simply used
driver.get(ViewElm.get_attribute('href'))
I would expect this to only work, however, if the element you are trying to click on is a link...
Re Tony Lâmpada's answer, comment #27 did indeed solve the problem for me, except that it provided Java code and I needed Python. Here's a Python function that scrolls to the element's position and then clicks it.
def scroll_to_and_click(xpath):
element = TestUtil.driver.find_element_by_xpath(xpath)
TestUtil.driver.execute_script('window.scrollTo(0, ' + str(element.location['y']) + ');')
element.click()
This solved the problem for me in Chrome 34.0. It caused no harm in Firefox 28.0 and IE 11; those browsers aren't subject to the problem, but scrolling to the element's position before clicking it still isn't a bad thing.
This might happen if the element changes position while the driver is attempting to click it (I've seen this with IE too). The driver retains the initial position but by the time it actually gets to clicking on it, that position is no longer pointing to that element. The FireFox driver doesn't have this problem BTW, apparently it "clicks" elements programmatically.
Anyway, this can happen when you use animations or simply change the height of elements dynamically (e.g. $("#foo").height(500)). You need to make sure that you only click elements after their height has "settled". I ended up with code that looks like this (C# bindings):
if (!(driver is FirefoxDriver))
{
new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(
d => d.FindElement(By.Id(someDynamicDiv)).Size.Height > initialSize);
}
In case of an animation or any other factor you can't easily query for, you can utilize a "generic" method that waits for the element to be stationary:
var prevLocation = new Point(Int32.MinValue, Int32.MinValue);
int stationaryCount = 0;
int desiredStationarySamples = 6; //3 seconds in total since the default interval is 500ms
return new WebDriverWait(driver, timeout).Until(d =>
{
var e = driver.FindElement(By.Id(someId));
if (e.Location == prevLocation)
{
stationaryCount++;
return stationaryCount == desiredStationarySamples;
}
prevLocation = e.Location;
stationaryCount = 0;
return false;
});
I met this because a loading dialog cover on this element. I simplely solve it by add a waiting before working with the this element.
try {
Thread.sleep((int) (3000));
} catch (InterruptedException e) {
//
e.printStackTrace();
}
Hope this help!
Explanation of error message:
The error message simply says, that the element you want to click on is present, but it is not visible. It could be covered by something or temporary not visible.
There could be many reasons why the element is not visible in the moment of the test. Please re-analyse your page and find proper solution for your case.
Solution for particular case:
In my case, this error occures, when a tooltip of the screen element i just clicked on, was poping over the element I wanted to click next. Defocus was a solution I needed.
Quick solution how to defocus would be to click to some other element in another part of the screen which does "nothing" resp. nothing happens after a click action.
Proper solution would be to call element.blur() on the element poping the tooltip, which would make the tooltip disapear.
The reason for this error is that the element that you are trying to click is not in the viewport (region seen by the user) of the browser. So the way to overcome this is by scrolling to the desired element first and then performing the click.
Javascript:
async scrollTo (webElement) {
await this.driver.executeScript('arguments[0].scrollIntoView(true)', webElement)
await this.driver.executeScript('window.scrollBy(0,-150)')
}
Java:
public void scrollTo (WebElement e) {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeAsyncScript('arguments[0].scrollIntoView(true)', e)
js.executeAsyncScript('window.scrollBy(0,-150)')
}
I was facing the same problem with clj-webdriver (clojure port of Selenium). I just translated the previous solution to clojure for convenience. You can call this function before doing click or whatever to avoid that problem.
(defn scrollTo
"Scrolls to the position of the given css selector if found"
[q]
(if (exists? q)
(let [ loc (location-once-visible q) jscript (str "window.scrollTo(" (:x loc) "," (:y loc) ")") ]
(execute-script jscript))))
Maybe it's not really clean solution but it works:
try:
el.click()
except WebDriverException as e:
if 'Element is not clickable at point' in e.msg:
self.browser.execute_script(
'$("{sel}").click()'.format(sel=el_selector)
)
else:
raise
I was getting this bug because I tested a hover and then needed to click on the link underneath the tooltip. The solution was to add page.find('.sp-logo').hover before click_link to get the tooltip out of the way.
It's funny, all the time I spent looking at the various responses, no one had tried the obvious, which of course, I hadn't either. If your page has the same id used multiple times, as mine did, ("newButton",) and the one you want is not the first one found, then you will in all likelihood get this error. The easiest thing to do (C#):
var testIt = driver.FindElements(By.Id("newButton"));
Note it's FindElements, not FindElement.
And then test to see how many results came back from the retrieval. If it's the second one, you can then use:
testit[1].Click();
Or get whomever reused ids to fix them.
After testing all mentioned suggestions, nothing worked. I made this code. It works, but is not beautiful
public void click(WebElement element) {
//https://code.google.com/p/selenium/issues/detail?id=2766 (fix)
while(true){
try{
element.click();
break;
}catch (Throwable e){
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
public void click(String css) {
//https://code.google.com/p/selenium/issues/detail?id=2766 (fix)
while(true){
try{
driver.findElement(By.cssSelector(css)).click();
break;
}catch (Throwable e){
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
I do a kind of brute force of clicks and it works for me.
try:
elem.click()
except:
print "failed to click"
size = elem.size
mid_of_y = int(size["height"])/2
stepts_to_do_to_left = int(size["width"])
while stepts_to_do_to_left > 0:
try:
print stepts_to_do_to_left, mid_of_y
action = webdriver.common.action_chains.ActionChains(driver)
action.move_to_element_with_offset(elem, mid_of_y, stepts_to_do_to_left)
action.click()
action.perform()
print "DONE CLICK"
break
except:
pass
If you have jQuery loaded on the page, you can execute the following javascript command:
"$('#" + element_id + "').click()"
Example using python executor:
driver.execute_script("$('#%s').click()" % element_id)
Try to maximize the browser when you are working with resolutions greater than 1024x768.
driver.manage().window().maximize();

Using Selenium, is there another, more reliable, way to use click command on an element in Headless Chrome?

After 2 days of struggling with an erratic click() command issue in Headless Chrome - in my case an Anchor (a) element with a href tag - and going through various threads advice about chromeOptions (--start-maximized, --window-size, etc...), and trying 12 different ways ( with sendKeys and Actions, and even submit() ) to work around the click() with no success...
Using ChromeDriver 77.0, Chrome 77.0.3865.75 and Selenium 3.141.59, my tests are stable in Chrome and they're unstable in Headless Chrome because of this erratic click():
E.g.: Click on an element (in my case an anchor (a) element with a href tag), in both Chrome and Headless Chrome, and check another element appears thereafter
Adding the loop and try catch below stabilizes my tests and makes their results reliable! Is there another way you can think of?
Test() {
for(int t = 0; t <= 2; t++) { //TRY CLICKING ON THE ELEMENT 3 TIMES
WebElement element =
wait.until(ExpectedConditions.presenceOfElementLocated(elementToFind));
wait.until(ExpectedConditions.visibilityOf(element));
wait.until(ExpectedConditions.elementToBeClickable(element));
try {
element.click(); //ERRATIC CLICK() ON HEADLESS CHROME
if(wait.until(ExpectedConditions.visibilityOfElementLocated(expectedElementAfterClick)).isDisplayed() == true)
break; //BUTTON WAS REALLY CLICKED
} catch (TimeoutException toe) { //BUTTON WASN'T REALLY CLICKED
if (t == 2) toe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
I'm saying "the click() is erratic in Headless Chrome" because the click() command always executes successfully (otherwise a NoSuchElement or a StaleElementReferenceException or any other exception would be found when clicking on the element) BUT, sometimes, the element is not actually clicked. Other times, the very same test and code runs smoothly and the element is actually clicked - I know this because the line with visibilityOfElementLocated(expectedElementAfterClick) executes as expected. This click() issue, in turn, makes my tests unstable. Thus, the results unreliable.
I suspect this to be an actual Selenium issue.
I've had rare circumstances like this too. You might try using ActionChains to do the click instead. It's been a while since I wrote anything in Java, but in Python, the code would look something like this:
# find your element using WebDriverWait, as you have above
ActionChains(self.driver).move_to_element(element).click().perform()
To keep it short and simple if your usecase is to invoke click() on a certain WebElement presenceOfElementLocated() and visibilityOf() won't help and you need to induce WebDriverWait for the elementToBeClickable() and you can use the following solution:
try {
new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(element).click();
System.out.println("Element was clicked")
}
catch(TimeoutException e) {
System.out.println("Element wasn't clicked")
}
You can find a detailed discussion in Selenium: Check for the presence of element
Additional consideration
Ensure that:
The Locator Strategies you have used, uniquely idendentifies the element within the DOM Tree.
JDK is upgraded to current levels JDK 8u222.
Selenium is upgraded to current levels Version 3.141.59.
ChromeDriver is updated to current ChromeDriver v77.0 level.
Chrome is updated to current Chrome Version 77.0 level. (as per ChromeDriver v77.0 release notes)

Selenium implicit and explicit wait, timeout exception element not found

I am new to selenium (but experienced java developer).
I am using something like below:
WebElement searchBasket = pDriver.findElement(By.xpath("//a[contains(.,'Search&Baskets')]"));
WebElement searchproduct = pDriver.findElement(By.xpath("//a[contains(.,'Search a product')]"));
//if search an agreement is not show up, then click on other menu, then click it back
pWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search&Baskets')]")));
pDriver.findElement(By.xpath("//a[contains(.,'Search&Baskets')]")).click();
// click on search an agreement
try {
pWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search&Baskets')]")));
action = new Actions(pDriver);
action.moveToElement(searchBasket).build().perform();
pWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search a product')]")));
searchproduct.click();
} catch (TimeoutException e) {
}
where pWait is:
WebDriverWait wait = new WebDriverWait(driver, 15);
however when I run the test case I get below error:
Unable to locate element: {"method":"xpath","selector":"//a[contains(.,'Search&Baskets')]"}
Command duration or timeout: 4 milliseconds
I thought it should have wait atleast 15 seconds before throwing this exception. From the log above it looks like it threw exception only in 4ms.
and i could see on console that as soon as it hit that line, it threw exception.
I have implicit waiting set as 0 and using explicit wait.
Am i missing anything here.
Also, in explicit and implicit wait, is it upto that much time OR exact that much time,
example if I set implicit wait as 10 sec, then does it mean wait for exact 10 sec OR wait upto 10 sec (if element found then proceed, even if element founf on 6th second)
is above same for explicit wait as well?
Please help
Let us analyze what is happening in our code.
We have defined two WebElements searchBasket and searchproduct as follows :
WebElement searchBasket = pDriver.findElement(By.xpath("//a[contains(.,'Search&Baskets')]"));
WebElement searchproduct = pDriver.findElement(By.xpath("//a[contains(.,'Search a product')]"));
We havn't tried to use those WebElements in our code at immediate basis, so had No Impact.
Next, we tried WebDriverWait for an WebElement as follows :
pWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search&Baskets')]")));
Again we didn't capture the return type of the result, so had No Impact.
Now, within the try{} block we have again tried WebDriverWait:
pWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search&Baskets')]")));
But again we havn't captured/acted on the return type of the result. That's why moving ahead when we did :
action.moveToElement(searchBasket).build().perform();
searchBasket referred to the WebElement which we have stored earlier as :
WebElement searchBasket = pDriver.findElement(By.xpath("//a[contains(.,'Search&Baskets')]"));
As this first search result (which was without WebDriverWait) may not have returned any WebElement at all and have returned Null.
Finally, the most important factor for the error Unable to locate element: {"method":"xpath","selector":"//a[contains(.,'Search&Baskets')]"} is, the
WebDriverWait instance was wait. Instead of using wait we have always tried to use pWait
So for all these reasons, WebDriverWait was never implemented properly in our code.
Mixing up ImplicitWait & ExplicitWait
The Selenium Documentation clearly mentions the following :
WARNING: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example setting an implicit wait of 10 seconds and an explicit wait of 15 seconds, could cause a timeout to occur after 20 seconds.
ExpectedConditions.elementToBeClickable invokes isDisplayed() and isEnabled() methods on EXISTING WebElement.
You are providing By as a parameter which means, the driver has to find your element first. It failed to do this.
Make sure your element is present by using wait until presenceOfElementLocatedBy(By by).
Example:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfElementLocatedBy(By.xpath("//a[contains(.,'Search&Baskets')]")));
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[contains(.,'Search&Baskets')]")));

Selenium Testing: Programming a more robust test

I am attempting to automate a task involving a JavaServer Page using the Selenium Webdriver. I have built a test that works about 90% of the time, meaning that the Webdriver makes it through all the tasks without any errors. During the test the browser executes the actions below:
opens a page
clicks on a link and switches windows
closes the original window
logs into a system
switches frames
clicks a link
fills out a form
submits the form
...more similar tasks
Unfortunatly, some of the time the Webdriver will randomly fail on one of the steps. This is because it could not find something, for example 'no frame found', 'unable to find element with link text...' and other similar errors. I am not sure why this happens sometimes and it appears to happen randomly at different stages in the test. I searched around for a solution and the best I could do was slow down the Webdriver with thread.sleep at various stages in the program. This seems like a very haphazard way to solve the problem to me and I am not even sure the problem is that the driver is moving too quickly. Even if I slow it down half a second I still sometimes get errors. I am a new user of Selenium and I do not know if this is usually a problem. Does anyone have a better way to address this problem? Am I overlooking something simple?
I did the same initially using thread sleep to develop my scripts. However, even though the scripts work while development, but it will be unrealiable on a production run as it fail inconsistently --which adds to the frustration. This is because thread sleep utilizes fixed waiting time and if elements do not get attached in time, it will fail the tests. singe31 has given a valid solution. Another solution would be by using fluentWait:
public void fluentWait(final By by)
{
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(by);
}
});
}
Here, the Selenium Webdriver will wait for at least 60 seconds to find an element until it throws of an TimeOutException. Otherwise, it will poll the DOM to find the element every 5 seconds and if the element is not found "NoSuchElementException" exception will be ignored.
Another problem which you are bound to face is, StaleStateReference or NoSuchElementException. You can check this link for solving that issue.
Hope it helps.
It's a common issue, instead of using Thread.sleep(duration) you should better create some utility method like
public WebElement waitForElementPresent(By by, int timeout) {
WebDriverWait wait = new WebDriverWait(driver, timeout);
WebElement element = null;
try {
element = wait.until(ExpectedConditions.presenceOfElementLocated(by));
} catch (TimeoutException e) {
e.printStackTrace();
}
return element;
}
This way if the element is already there you won't wait, and if it's loading it will wait until it finds it or until the timeout is triggered

Webdriver, detect DOM changing and wait for it

I am using Webdriver in Java and I encountered an issue repeatedly that I can't find a proper solution yet.
It is to do with doing actions on a page that will cause this page DOM to change (for example, Javascript lightbox), then my JUnit test is expecting new elements after the DOM change but my test is getting the old DOM element.
To give you an example, I have a scenario as below.
First of all click “Add item” button in the below image and the light box appears:
Then fill in all the item details and click "Add & Close". You will see the screen below:
Notice that now there is an info message Your item ... has been added.
Now I put keywords in the Search text box and hit enter and the info message will be changed to below:
In my JUnit test, the flow is like below:
....
itemDetailsPage.clickAddAndClose();
itemDetailsPage.searchItemBy("Electricity");
assertEquals("Your search for 'electricity' returned 2 results.",
itemDetailsPage.getInfoMsg());
....
Now this test is not very robust, because if the network is slow, most of the times, getInfoMsg() will return the previous info message Your item ... has been added instead of the latest info message, which causes the test to fail. Just a side note that these two info message have share the same html element id.
The solution I am trying to implement here are:
add explicit wait in clickAddAndClose()
So it looks something like:
public void clickAddAndClose() {
...
clickWhenReady(driver, By.id(addAndCloseButtonId));
...
waitForElementByLocator(driver,By.id(itemInfoMsgId),10);
}
The second wait proves to be useless because, itemInfoMsgId already exist when the user added the item from the add item lightbox.
add waitForPageLoaded() method at the end of clickAddAndClose() to try to wait for the page to finish reloading. The generic method for waitForPageLoaded() below:
public void waitForPageLoaded(WebDriver driver) {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState").equals("complete");
}
};
Wait<WebDriver> wait = new WebDriverWait(driver, 30);
try {
wait.until(expectation);
} catch (Throwable error) {
assertFalse("Timeout waiting for Page Load Request to complete.",
true);
}
}
I am expect at the end of clickAddAndClose(), it will see this page is still being updated so it will wait until the info message has been updated. But this does not seem to work either.
That leaves me to the last choice will is to add a thread sleep at the end of clickAddAndClose(). I want to avoid using it.
Is there a generic way of solving this kind of problem? How do I detect that the page DOM is still changing and tell Webdriver to wait until it finishes refreshing?
Waiting for the page to be loaded won't work if (as it seems to be the case) your page is being modified by AJAX operations.
Instead of waiting for the page to load, wait for the condition you are testing to become true. This way, you give the AJAX operation time to execute and if your there is a problem you will get an error when the time out occurs.
I usually use the Python bindings for Selenium and it has been quite a while since I wrote Java code but I believe it would look something like this, with X being replaced with a type appropriate for the itemDetailsPage object:
new FluentWait<X>(itemDetailsPage)
.until(new Function<X, Boolean>() {
public Boolean apply(X itemDetailsPage) {
return "Your search for 'electricity' returned 2 results." == itemDetailsPage.getInfoMsg();
};
});
Seems like you need to wait until ajax has finished its job. In a similar situation I've used a method similar to waitForJQueryProcessing described here. Take a look, it might help.

Categories