I am using WebDriver with Java binding. I am using generic methods for element waiting. One of them is called waitByPageTitle.
Here is my definition for this method:
public void waitByPageTitle(WebDriver driver, String pageTitle) {
WebDriverWait wait = new WebDriverWait(driver, DEFAULT_IMPLICIT_WAIT);
try {
wait.until(ExpectedConditions.titleContains(pageTitle));
} catch (TimeoutException e) {
...
}
}
In my page objects, when a method needs to wait by a page title, I pass arguments to this method. But there are scenarios where the page title can be different based on different events.
How do I change the generic waitByPageTitle method so it accepts multiple arguments, and can wait by any one of them which ever it sees the first?
Thanks.
You can use FluentWait and Java varargs
// This method will accept any number of titles
public void waitUntilTextChanges(WebDriver driver, String... titles) {
new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(10, TimeUnit.MILLISECONDS)
.until(new Predicate<WebDriver>() {
public boolean apply(WebDriver d) {
boolean titleMatched = false;
// Get current window title
String windowTitle = driver.getTitle();
for(String title : titles){
// Iterate through all input titles and compare with window title
titleMatched = windowTitle.equalsIgnoreCase(title);
// If match found, exit
if(titleMatched){
break;
}
}
return titleMatched;
}
});
}
Related
I need to wait for a specific loader to complete loading once the button has been pressed, please take a look at the following image below:
As you can see from the image above, once the button has been pressed the ajax loader appears inside the button.
I have created the following selector to locate the button:
//form[contains(#id, 'messageform')]//button/span
Currently accepting the request (Clicking on the button) fails my test case as the script continues to the next test steps without waiting for the loader to complete.
I have tried the following and more, with no luck:
Injecting JS to wait for the page to fully load.
ExpectedCondition<Boolean> expectation = driver -> ((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete");
ExpectedConditions.invisibilityOf(element)
WebDriver driver = getDriver();
WebDriverWait exists = new WebDriverWait(driver, timer);
exists.until(ExpectedConditions.refreshed(
ExpectedConditions.invisibilityOf(element)));
Any ideas?
You should use .stalenessOf() to wait until an element is no longer attached to the DOM.
Something like this (tweak to your case):
WebElement somePageElement = driver.findElement(By.id("someId"));
WebDriverWait wait = new WebDriverWait(webDriver, 10);
// do something that changes state of somePageElement
wait.until(ExpectedConditions.stalenessOf(somePageElement));
And the good thing is you don't have to handle any exceptions.
Alternatively, you can also create a method and handle exceptions like so:
public static void WaitForCommission (WebDriver driver) throws Exception {
for (int second = 0; second++) {
if (second >= 30) fail("timeout");
try {
if (IsElementActive(By.id("someElementID"), driver))
break;
} catch (Exception e) {}
Thread.sleep(1000);
}
}
private static boolean IsElementActive(By id, WebDriver driver) {
WebElement we = driver.findElement(id);
if(we.isEnabled())
return true;
return false;
}
In Java, Selenium, you can wait until a text is present in a webelement (with a WebDriverWait):
wait.until(ExpectedConditions.textToBePresentInElement(webelement, expectedMessage));
However, what do you do when you don't want just expectedMessage to be present in the element (= expectedMessage being a substring of webelement.getText()), but to be the exact text of the webelement (=expectedMessage being the same string as webelement.getText())?
Selenium does provide the function:
wait.until(ExpectedConditions.textToBe(locator, expectedMessage));
but when you have gathered webelements by the locators with #FindBy in your page class, it's awkward to make the locators again directly accessible to test classes.
How can this be solved?
You can create your own ExpectedCondition:
public static ExpectedCondition<Boolean> waitForTextInElementEquals(WebElement elm, String text) {
return new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
try {
String elementText = elm.getText();
return elementText.equals(text);
} catch (StaleElementReferenceException var3) {
return null;
}
}
public String toString() {
return String.format("text ('%s') to be present in element %s", text, elm);
}
};
}
Which you can use just like the ExpectedConditions already in WebDriverWait:
WebDriverWait wait = new WebDriverWait(WebDriver, 30, 1000);
wait.until(waitForTextInElementEquals(foo, bar));
There's another simpler solution:
WebDriverWait wait = new WebDriverWait(webdriver, waitForElementTimeout).until(ExpectedConditions.attributeToBe(webelement, "text", expected));
Tested with selenium 3.8.1.
I am using this code to check for invisibility:
WebDriverWait wait = new WebDriverWait(driver,40);
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath(<some xpath>)));
This works perfectly if there is only one element corresponding to the xpath in the webpage.
I have three in the webpage which I am trying to write a script for, and I need selenium to wait for all three.
Note: I am not using absolute xpath.
ExpectedConditions.invisibilityOfElementLocated check for the first element. In your case you could write your own implementation of ExpectedCondition where you have to check if the object is displayed for each of the element which is found.
For Example (not tested) :
private static void waitTillAllVisible(WebDriverWait wait, By locator) {
wait.until(new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(WebDriver driver) {
Iterator<WebElement> eleIterator = driver.findElements(locator).iterator();
while (eleIterator.hasNext()) {
boolean displayed = false;
try {
displayed = eleIterator.next().isDisplayed();
}
catch (NoSuchElementException | StaleElementReferenceException e) {
// 'No such element' or 'Stale' means element is not available on the page
displayed = false;
}
if (displayed) {
// return false even if one of them is displayed.
return false;
}
}
// this means all are not displayed/invisible
return true;
}
});
}
I know there are several threads regarding this topic, but I am not necessarily looking for a solution, but rather an explanation. I work on a very large automation suite that tests a web application via mobile phones using browserstack. My stability is very low.. and it is due to this error getting thrown at me! Occasionally it will work and occasionally it will not.. I can not use Actions because Browserstack does not support that.. WHY does this error exist and has anyone had any success it working around it. I always wait for an object using wait.until(ExpectedConditions), but sometimes this does not work well enough. I cant quite catch it as an exception since it is an Unknown error. Also, our standards do not allow for a Thread.sleep(). Any ideas? Thank you so much
And here is a screen of some code..
You are waiting for a WebElement to be clickable, then again you are finding a list of WebElements and clicking the first element.
This does not guarantee that you are clicking the element you waited for it be clickable.
public void waitAndClickElement(WebElement element) {
driverWait.until(ExpectedConditions.elementToBeClickable(element)).click();
}
In your case,
public void clickImageView() {
driverWait.until(ExpectedConditions.elementToBeClickable(listImageView)).click() ;
}
Element is normally not click able due to following reasons .
Html is loading and client is still receiving updates from server
When Scrolling
It can be due to some object is overlapping target
problem 3 can not be resolved you need to fix your code in this wait i wait for HTML to ready and then verify is it click able or not this has eliminated such exceptions from my code
how ever i made a solution for problem 1 and 2 you can simply use my custom wait before clicking . call this function
public static void waitForElementPresent(final By by, int timeout,WebDriver driver)
After this if you are using browser other then chrome then call Scroll to that object this would fix you problem
Code
public static void waitForElementPresent(final By by, int timeout,WebDriver driver) {
waitForPageLoad(driver);
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,40).ignoring(StaleElementReferenceException.class);
/* wait.until(new ExpectedCondition<Boolean>(){
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
}); */
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
wait.until(ExpectedConditions.presenceOfElementLocated(by));
wait.until(ExpectedConditions.elementToBeClickable(by));
WebDriverWait wait2 = new WebDriverWait(driver, 40);
wait2.until(ExpectedConditions.elementToBeClickable(by));
}
//wait for page to laod
public static void waitForPageLoad(WebDriver driver) {
ExpectedCondition<Boolean> pageLoadCondition = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
}
};
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(pageLoadCondition);
}
This is due to the speed at which selenium runs. It will try and find elements before the page has loaded, thus resulting in this error.
For the code sample you provided, the .until() returns the WebElement you are waiting for. You can use the code below to click it rather than scraping the page again.
public void clickImageView()
{
driverWait.until(ExpectedConditions.elementToBeClickable(listImageView)).click();
}
The Webpage contains 2 frames.
My Selenium script :
#FindBy(how = How.XPATH, using = ".//iframe") private List <WebElement> framesList;
public myPage (WebDriver driver){
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void goFrame2() {
if (framesList.size() >= 1) {
driver.switchTo().frame(framesList.get(1));
}
}
When I execute the script, the size of the liste framesList is always 1. Just one frame is detected. If I add a Thread.sleep of 5 seconds before PageFactory.initElements(driver, this); the second frame is detected and the size of the list is 2.
Of course I don't want to use a Thread.sleep.
How to detect the 2 frames properly?
Julien
You should use AjaxElementLocatorFactory
Create Constructor of class like below:
public class Login{
public Login(WebDriver driver) {
// TODO Auto-generated constructor stub
this.driver = driver;
PageFactory.initElements(new AjaxElementLocatorFactory(driver, 20), this);
}
}
It will wait for elements for specified (20 secs) time.
So, you should explicitely find the xpath/css selector of the second frame (it will definitely differ from first one) and to wait untill it will appear. Only after that you can use your code. Here is an example how you can wait for frame containing class named "class":
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("frame.someclass")));