I have a responsive design, so many items I need to interact with have 2 WebElements. One for Desktop and one for Mobile, and I am trying to use PageFactory. Here's what I have now to identify and interact with the element.
//this returns 2 webelements, one for desktop and one for mobile
#FindBy(xpath = "//selector-dropdown/p")
private WebElement dropdown;
public void ClickDropdown() throws InterruptedException {
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.visibilityOf(dropdown)).click();
}
I was under the impression that ExpectedConditions.visibilityOf(WebElement) would find the first element visible. Right now when I open the app on Desktop, it finds the element (Desktop is first in the DOM). But on Mobile, it times out waiting for visibility, because it is stuck waiting for the first one to become visible.
My alternative is using #FindBy to declare every element twice, and making an if statement to decide which path to take. Is this extra work the only way to make it work?
Your assumption "... ExpectedConditions.visibilityOf(WebElement) would find the first element visible." is wrong! You will need to declare your WebElement as a List, find all of them, and pick the one that is visible. Here is a sample that I used successfully in the past:
#FindBy(xpath = "//input[#ng-model='loginData.username']")
private List<WebElement> txtUsername;
public String getUsername() {
driverWait.until(CustomExpectedConditions.presenceOfAllElements(txtUsername));
for (WebElement oneUsername : txtUsername) {
if (oneUsername.isDisplayed())
return oneUsername.getAttribute("value");
}
return null;
}
public void setUsername(String username) {
driverWait.until(CustomExpectedConditions.presenceOfAllElements(txtUsername));
for (WebElement oneUsername : txtUsername) {
if (oneUsername.isDisplayed()) {
oneUsername.clear();
oneUsername.sendKeys(username);
break;
}
}
}
Based on discussion elsewhere, here is the CustomExpectedConditions:
public class CustomExpectedConditions {
/**
* Based on {#link ExpectedConditions#presenceOfAllElementsLocatedBy(org.openqa.selenium.By)}.
*
* #param elements
* #return
*/
public static ExpectedCondition<List<WebElement>> presenceOfAllElements(
final List<WebElement> elements) {
return new ExpectedCondition<List<WebElement>>() {
#Override
public List<WebElement> apply(WebDriver driver) {
// List<WebElement> elements = findElements(locator, driver);
return elements.size() > 0 ? elements : null;
}
};
}
}
Combining #Andersons and #SiKing answers into a solution that can be used everywhere, you could make a method available to all of your PageObjects in a base class, which you might already have:
protected WebElement getVisibleElement(List<WebElement> elements)
{
//Need a guard clause here to ensure there are exactly two elements,
//Or make the wait check for all elements more safely. Either way,
//consider changing the method name to be clear about expectations
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(
ExpectedConditions.or(
//This should be done more safely, unless already guarded to expect 2 elements
ExpectedConditions.visibilityOf(elements.get(0))),
ExpectedConditions.visibilityOf(elements.get(1)))
)
);
for (WebElement element : elements) {
if (element.isDisplayed())
{
return element;
}
}
//Throw element not visible exception or something
}
Then in your PageObject:
#FindBy(xpath = "//selector-dropdown/p")
private List<WebElement> dropdown;
public void ClickDropdown() throws InterruptedException {
getVisibleElement(dropdown)).click();
}
As I know in Java you can combine multiple conditions with ExpectedConditions.and() as below:
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(
ExpectedConditions.and(
ExpectedConditions.visibilityOf(driver.findElement(By.xpath("(//selector-dropdown/p)[1]"))),
ExpectedConditions.visibilityOf(driver.findElement(By.xpath("(//selector-dropdown/p)[2]" )))
)
);
or just try
ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("//selector-dropdown/p"));
to wait until all elements matched by specified selector became visible
Also try to look for atribute in DOM, which makes the element visible. That's the fastes and should be prefered aproach.
For example your element in DOM might be or sit under element with 'display: none;' or 'position: fixed'
Then your x-path might for example look like:
//div[not(contains(#style,'display: none;')) ...
or
//div[contains(#style,'position: fixed') ...
Or there might be something else.
DOM by it self always tells you which element is visible and which is not.
This is the method I came up with, maybe someone can use the full solution. This will A. wait for the first element in a list to be visible, then B. assign the visible one as a single element. The wait is short because it shouldn't take more than a second to figure out which element is visible.
protected WebElement getVisibleElement(List<WebElement> elements) {
WebDriverWait wait = new WebDriverWait(driver, 1);
WebElement rE = null;
int elementsSize = elements.size();
for (int i = 0; i < elementsSize; i++) {
System.out.println("test" + i);
try {
wait.until(ExpectedConditions.or(ExpectedConditions.visibilityOf(elements.get(i))));
break;
} catch (Exception e) {
//handle exception however you like
}
}
for (WebElement element : elements) {
if (element.isDisplayed()) {
System.out.println("found and assigning to rE");
rE = element;
break;
}
}
return rE;
}
Is there an elegant way to get the By locator of a Selenium WebElement, that I already found/identified?
To be clear about the question: I want the "By locator" as used to find the element. I am in this case not interested in a specific attribute or a specific locator like the css-locator.
I know that I could parse the result of a WebElement's toString() method:
WebElement element = driver.findElement(By.id("myPreciousElement"));
System.out.println(element.toString());
Output would be for example:
[[FirefoxDriver: firefox on WINDOWS (....)] -> id: myPreciousElement]
if you found your element by xpath:
WebElement element = driver.findElement(By.xpath("//div[#someId = 'someValue']"));
System.out.println(element.toString());
Then your output will be:
[[FirefoxDriver: firefox on WINDOWS (....)] -> xpath: //div[#someId = 'someValue']]
So I currently wrote my own method that parses this output and gives me the "recreated" By locator.
BUT is there a more elegant way already implemented in Selenium to get the By locator used to find the element? I couldn't find one so far.
If you are sure, there is none out of the box, can you think of any reason why the API creators might not provide this functionality?
*Despite the fact that this has nothing to do with the question, if someone wonders why you would ever need this functionality, just 2 examples:
if you use PageFactory you most likely will not have the locators as as member variables in your Page class, but you might need them later on when working with the page's elements.
you're working with APIs of people who just use the Page Object Pattern without PageFactory and thus expect you to hand over locators instead of the element itself.*
tldr; Not by default, no. You cannot extract a By from a previously found WebElement. It is possible, however, through a custom solution.
It's possible to implement a custom solution, but Selenium does not offer this out-of-the-box.
Consider the following, on "why"..
By by = By.id("someId");
WebElement e = driver.findElement(by);
you already have the By object, so you wouldn't need to call something like e.getBy()
No, there's not. I have implemented a possible solution as a proxy:
public class RefreshableWebElement implements WebElement {
public RefreshableWebElement(Driver driver, By by) {
this.driver = driver;
this.by = by;
}
// ...
public WebElement getElement() {
return driver.findElement(by);
}
public void click() {
getElement().click();
}
// other methods here
}
There is no elegant way provided by Selenium. And this is horrible
1) PageObject and PageFactory implies that we have WebElements in Page classes, but we don't have locators of those elements.
2) If I find element as descendant of current element using webElement.findElement(By), then I don't have the locator of this descendant even if I stored parent's locator in the variable.
3) If I use findElements function that returns List of elemetns, then I don't have locator for each specific element.
4) Having locator for element is useful at least because ExpectedConditions with locator as parameter are much better implemented than ExpectedConditions with WebElement as parameter.
For me Selenium is ill-conceived and poorly implemented library
Currently there is no specific method from selenium's end to do so. What you can do is write your custom method. You will get the clue of what selector type and path is used by just printing the webElement you have.
It looks something like this
[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[#title='Search']]
Now, what you need to do is to extract the locator and its value. You can try something like this
private By getByFromElement(WebElement element) {
By by = null;
//[[ChromeDriver: chrome on XP (d85e7e220b2ec51b7faf42210816285e)] -> xpath: //input[#title='Search']]
String[] pathVariables = (element.toString().split("->")[1].replaceFirst("(?s)(.*)\\]", "$1" + "")).split(":");
String selector = pathVariables[0].trim();
String value = pathVariables[1].trim();
switch (selector) {
case "id":
by = By.id(value);
break;
case "className":
by = By.className(value);
break;
case "tagName":
by = By.tagName(value);
break;
case "xpath":
by = By.xpath(value);
break;
case "cssSelector":
by = By.cssSelector(value);
break;
case "linkText":
by = By.linkText(value);
break;
case "name":
by = By.name(value);
break;
case "partialLinkText":
by = By.partialLinkText(value);
break;
default:
throw new IllegalStateException("locator : " + selector + " not found!!!");
}
return by;
}
For me worked with commons-lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
For remote web element use method like:
protected String getLocator(WebElement element) {
try {
Object proxyOrigin = FieldUtils.readField(element, "h", true);
Object locator = FieldUtils.readField(proxyOrigin, "locator", true);
Object findBy = FieldUtils.readField(locator, "by", true);
if (findBy != null) {
return findBy.toString();
}
} catch (IllegalAccessException ignored) {
}
return "[unknown]";
}
I had written this utility function which returns a string combination of locator strategy + locator value.
private String getLocatorFromWebElement(WebElement element) {
return element.toString().split("->")[1].replaceFirst("(?s)(.*)\\]", "$1" + "");
}
My solution when I ran into needing the By locator to use for an ExpectedConditions and I had my locators in the Page Object Factory was to use a String that had the locator in it and then build my By object and the element locator from that.
public class PageObject {
private static final String XPATH_NAME = "...";
public #iOSXCUITFindBy(xpath = XPATH_NAME)
List<MobileElement> mobileElementName;
public By getByXPath(){
return new By.ByXPath(XPATH_NAME);
}
public PageObject() {
PageFactory.initElements(driver, this);
}
}
I am automating a web application right now.I have used a list array to locate few objects inside a container.All I need to do is that that I have to mouse-hover to the first element and click on the same.But the mouse-hover method I have written in another class as common function.So can I use the object of the list array to pass to the mouse-hover method in any way.?
To Find the elements in the container .
By by = By.xpath("//ul[#id='sortable']");
List<WebElement> featureList= element.findElements(by.tagName("a"));
//Mouse-hover method
public static void moveMouseOver(WebDriver driver, By locator) {
WebElement element = waitForElementPresent(driver, locator);
(new Actions(driver)).moveToElement(element).build().perform();
}
Here can I change the 'By Locator' argument to replace with List array object ?
You could try changing moveMouseOver to something like :
public static void moveMouseOver(WebDriver driver, WebElement... webElements){
if(null != webElements){
for(WebElement webEl : webElements){
// do something here
}
}
}
and then call this as
moveMouseOver(driver, ((WebElement[])featureList.toArray()))
Please check for syntax errors if any as I have written this up here only and not checked in an IDE
You can change your method to:
public static void moveMouseOver(WebDriver driver, By locator, String...action) {
List<WebElement> lstElements = driver.findElements(locator);
for (WebElement webelement : lstElements){
if (action.length > 0 && action.equalsIgnoreCase("click"))
(new Actions(driver)).moveToElement(element).click().build().perform();
else
(new Actions(driver)).moveToElement(element).build().perform();
}
In such a scenario it will work for single element also and for multiple and you won't be required to change usage in previous scenarios, though u need handle different cases in future as this one handles click only.
How can I use WebDriverWait to wait until an attribute changes?
In my AUT I have to wait for a button to become enabled before continuing, unfortunately due to the way the developer coded the page I can't use WebElement's isEnabled() method. The developer is using some CSS to just make the button look like it's disabled so the user can't click on it and the method isEnabled always returns true for me. So what I have to do is get the attribute "aria-disabled" and check whether the text is "true" or "false". What I've been doing so far is a for loop with Thread.sleep, something like this:
for(int i=0; i<6; ++i){
WebElement button = driver.findElement(By.xpath("xpath"));
String enabled = button.getText()
if(enabled.equals("true")){ break; }
Thread.sleep(10000);
}
(disregard the code above if incorrect, just pseudo code of what I'm doing)
I'm sure there's a way to achieve something similar using WebDriverWait which is the preferred method I just can't figure out how. This is what I'm trying to achieve even though the following won't work:
WebDriverWait wait = new WebDriverWait(driver, 60);
wait.until(ExpectedConditions.visibilityOf(refresh.getText() == "true"));
Obviously it doesn't work because the function is expecting a WebElement not String but it's what I'm trying to evaluate. Any ideas?
The following might help your requirement.
In the following code, we override apply method incorporating the condition that we are looking for. So, as long as the condition is not true, in our case, the enabled is not true, we go in a loop for a maximum of 10 seconds, polling every 500 ms (this is the default) until the apply method returns true.
WebDriverWait wait = new WebDriverWait(driver,10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
WebElement button = driver.findElement(By.xpath("xpath"));
String enabled = button.getAttribute("aria-disabled");
if(enabled.equals("true"))
return true;
else
return false;
}
});
If someone wants to use #Sri as a method in a Selenium wrapper, here is a way to do it (thanks to this answer btw):
public void waitForAttributeChanged(By locator, String attr, String initialValue) {
WebDriverWait wait = new WebDriverWait(this.driver, 5);
wait.until(new ExpectedCondition<Boolean>() {
private By locator;
private String attr;
private String initialValue;
private ExpectedCondition<Boolean> init( By locator, String attr, String initialValue ) {
this.locator = locator;
this.attr = attr;
this.initialValue = initialValue;
return this;
}
public Boolean apply(WebDriver driver) {
WebElement button = driver.findElement(this.locator);
String enabled = button.getAttribute(this.attr);
if(enabled.equals(this.initialValue))
return false;
else
return true;
}
}.init(locator, attr, initialValue));
}
You can use the xpath to define the element with an attribute of your desired value and then wait until it occurs on the page.
In Python it could look like this:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
xp = '//*[#id="element_id"][#aria-disabled="false"]'
WebDriverWait(browser, 60).until(EC.presence_of_element_located((By.XPATH, xp)))
This worked for me when automating against a pretty slow bootstrap page waiting for the sort button to take effect.
new WebDriverWait(webDriver, 10)
.until(ExpectedConditions.attributeContains(BUTTON_SORT_ASCENDING_PRICE, "class", "sortButtonActive"));
"webDriver" - my current instance of WebDriver
"10" - timeout seconds
"BUTTON_SORT_ASCENDING_PRICE" - The By locator for the element
"class" - the attribute
"sortButtonActive" - the value of 'class' i am waiting for
Selenium V3.01
It can also be done using a simple do-while loop, System.currentTimeMillis(); method can be used to avoid an infinite loop
long startTime = System.currentTimeMillis();
String disabled;
do{
disabled = element.getAttribute("attributeName").trim();
}while(disabled!="false" && System.currentTimeMillis()-startTime<10000);
How do you make Selenium 2.0 wait for the page to load?
You can also check pageloaded using following code
IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Use class WebDriverWait
Also see here
You can expect to show some element. something like in C#:
WebDriver _driver = new WebDriver();
WebDriverWait _wait = new WebDriverWait(_driver, new TimeSpan(0, 1, 0));
_wait.Until(d => d.FindElement(By.Id("Id_Your_UIElement")));
If you set the implicit wait of the driver, then call the findElement method on an element you expect to be on the loaded page, the WebDriver will poll for that element until it finds the element or reaches the time out value.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
source: implicit-waits
In general, with Selenium 2.0 the web driver should only return control to the calling code once it has determined that the page has loaded. If it does not, you can call waitforelemement, which cycles round calling findelement until it is found or times out (time out can be set).
Ruby implementation:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
#driver.execute_script("return document.readyState;") == "complete"
}
All of these solutions are OK for specific cases, but they suffer from at least one of a couple of possible problems:
They are not generic enough -- they want you to know, ahead of time, that some specific condition will be true of the page you are going to (eg some element will be displayed)
They are open to a race condition where you use an element that is actually present on the old page as well as the new page.
Here's my attempt at a generic solution that avoids this problem (in Python):
First, a generic "wait" function (use a WebDriverWait if you like, I find them ugly):
def wait_for(condition_function):
start_time = time.time()
while time.time() < start_time + 3:
if condition_function():
return True
else:
time.sleep(0.1)
raise Exception('Timeout waiting for {}'.format(condition_function.__name__))
Next, the solution relies on the fact that selenium records an (internal) id-number for all elements on a page, including the top-level <html> element. When a page refreshes or loads, it gets a new html element with a new ID.
So, assuming you want to click on a link with text "my link" for example:
old_page = browser.find_element_by_tag_name('html')
browser.find_element_by_link_text('my link').click()
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
For more Pythonic, reusable, generic helper, you can make a context manager:
from contextlib import contextmanager
#contextmanager
def wait_for_page_load(browser):
old_page = browser.find_element_by_tag_name('html')
yield
def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
wait_for(page_has_loaded)
And then you can use it on pretty much any selenium interaction:
with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()
I reckon that's bulletproof! What do you think?
More info in a blog post about it here
You may remove the System.out line. It is added for debug purposes.
WebDriver driver_;
public void waitForPageLoad() {
Wait<WebDriver> wait = new WebDriverWait(driver_, 30);
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
System.out.println("Current Window State : "
+ String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState")));
return String
.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"))
.equals("complete");
}
});
}
Here is a Java 8 version of the currently most upvoted answer:
WebDriverWait wait = new WebDriverWait(myDriver, Duration.ofSeconds(15));
wait.until(webDriver -> "complete".equals(((JavascriptExecutor) webDriver)
.executeScript("return document.readyState")));
Where myDriver is a WebDriver object (declared earlier).
Note: Be aware that this method (document.readyState) only checks the DOM.
You can also use the class: ExpectedConditions to explicitly wait for an element to show up on the webpage before you can take any action further actions
You can use the ExpectedConditions class to determine if an element is visible:
WebElement element = (new WebDriverWait(getDriver(), 10)).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input#houseName")));
See ExpectedConditions class Javadoc for list of all conditions you are able to check.
Imran's answer rehashed for Java 7:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState"
).equals("complete");
}
});
This seems to be a serious limitation of WebDriver. Obviously waiting for an element will not imply the page being loaded, in particular the DOM can be fully build (onready state) whereby JS is still executing and CSS and images are still loading.
I believe the simplest solution is to set a JS variable upon the onload event after everything is initialized and check and wait for this JS variable in Selenium.
If you want to wait for a specific element to load, you can use the isDisplayed() method on a RenderedWebElement :
// Sleep until the div we want is visible or 5 seconds is over
long end = System.currentTimeMillis() + 5000;
while (System.currentTimeMillis() < end) {
// Browsers which render content (such as Firefox and IE) return "RenderedWebElements"
RenderedWebElement resultsDiv = (RenderedWebElement) driver.findElement(By.className("gac_m"));
// If results have been returned, the results are displayed in a drop down.
if (resultsDiv.isDisplayed()) {
break;
}
}
(Example from The 5 Minute Getting Started Guide)
Man all these answers require too much code. This should be a simple thing as its pretty common.
Why not just inject some simple Javascript with the webdriver and check.
This is the method I use in my webscraper class. The Javascript is pretty basic even if you don't know it.
def js_get_page_state(self):
"""
Javascript for getting document.readyState
:return: Pages state. See doc link below.
"""
ready_state = self.driver.execute_script('return document.readyState')
if ready_state == 'loading':
self.logger.info("Loading Page...")
elif ready_state == 'interactive':
self.logger.info("Page is interactive")
elif ready_state == 'complete':
self.logger.info("The page is fully loaded!")
return ready_state
More Info in "Document.readyState" of MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
Explicitly wait or conditional wait in this wait until given this condition.
WebDriverWait wait = new WebDriverWait(wb, 60);
wait.until(ExpectedConditions.elementToBeClickable(By.name("value")));
This will wait for every web element for 60 seconds.
Use implicitly wait for wait of every element on page till that given time.
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
This will wait for every web element for 60 seconds.
I'm surprised that predicates weren't the first choice as you typically know what element(s) you will next interact with on the page you're waiting to load. My approach has always been to build out predicates/functions like waitForElementByID(String id) and waitForElemetVisibleByClass(String className), etc. and then use and reuse these wherever I need them, be it for a page load or page content change I'm waiting on.
For example,
In my test class:
driverWait.until(textIsPresent("expectedText");
In my test class parent:
protected Predicate<WebDriver> textIsPresent(String text){
final String t = text;
return new Predicate<WebDriver>(){
public boolean apply(WebDriver driver){
return isTextPresent(t);
}
};
}
protected boolean isTextPresent(String text){
return driver.getPageSource().contains(text);
}
Though this seems like a lot, it takes care of checking repeatedly for you
and the interval for how often to check can be set along with the ultimate
wait time before timing out. Also, you will reuse such methods.
In this example, the parent class defined and initiated the WebDriver driver and the WebDriverWait driverWait.
I hope this helps.
Use implicitly wait for wait of every element on page till given time.
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
this wait for every element on page for 30 sec.
Another wait is Explicitly wait or conditional wait in this wait until given condition.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
In id give static element id which is diffidently display on the page, as soon as page is load.
The best way to wait for page loads when using the Java bindings for WebDriver is to use the Page Object design pattern with PageFactory. This allows you to utilize the AjaxElementLocatorFactory which to put it simply acts as a global wait for all of your elements. It has limitations on elements such as drop-boxes or complex javascript transitions but it will drastically reduce the amount of code needed and speed up test times. A good example can be found in this blogpost. Basic understanding of Core Java is assumed.
http://startingwithseleniumwebdriver.blogspot.ro/2015/02/wait-in-page-factory.html
Call below Function in your script , this will wait till page is not loaded using javascript
public static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
NodeJS Solution:
In Nodejs you can get it via promises...
If you write this code, you can be sure that the page is fully loaded when you get to the then...
driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));
If you write this code, you will navigate, and selenium will wait 3 seconds...
driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...
From Selenium Documentation (Nodejs):
this.get( url ) → Thenable<undefined>
Schedules a command to navigate to the given URL.
Returns a promise that will be resolved when the document has finished loading.
You can use the below existing method to set the pageLoadTimeout. In below example if the page is taking more than 20 seconds to load, then it will throw an exception of page reload:
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
/**
* Call this method before an event that will change the page.
*/
private void beforePageLoad() {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("document.mpPageReloaded='notYet';");
}
/**
* Call this method after an event that will change the page.
*
* #see #beforePageLoad
*
* Waits for the previous page to disappear.
*/
private void afterPageLoad() throws Exception {
(new WebDriverWait(driver, 10)).until(new Predicate<WebDriver>() {
#Override
public boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
Object obj = js.executeScript("return document.mpPageReloaded;");
if (obj == null) {
return true;
}
String str = (String) obj;
if (!str.equals("notYet")) {
return true;
}
return false;
}
});
}
You can change from the document to an element, in the case of where only part of a document is being changed.
This technique was inspired by the answer from sincebasic.
SeleniumWaiter:
import com.google.common.base.Function;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
public class SeleniumWaiter {
private WebDriver driver;
public SeleniumWaiter(WebDriver driver) {
this.driver = driver;
}
public WebElement waitForMe(By locatorname, int timeout){
WebDriverWait wait = new WebDriverWait(driver, timeout);
return wait.until(SeleniumWaiter.presenceOfElementLocated(locatorname));
}
public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {
// TODO Auto-generated method stub
return new Function<WebDriver, WebElement>() {
#Override
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
};
}
}
And to you use it:
_waiter = new SeleniumWaiter(_driver);
try {
_waiter.waitForMe(By.xpath("//..."), 10);
}
catch (Exception e) {
// Error
}
You can explicitly wait for an element to show up on the webpage before you can take any action (like element.click()):
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(new ExpectedCondition<WebElement>() {
#Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}
}
);
This is what I used for a similar scenario and it works fine.
My simple way:
long timeOut = 5000;
long end = System.currentTimeMillis() + timeOut;
while (System.currentTimeMillis() < end) {
if (String.valueOf(
((JavascriptExecutor) driver)
.executeScript("return document.readyState"))
.equals("complete")) {
break;
}
}
You can use this snippet of code for the page to load:
IWait wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver,TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
Or you can use waiter for any element to be loaded and become visible/clickable on that page, most probably which is going to be load at the end of loading like:
Wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath(xpathOfElement));
var element = GlobalDriver.FindElement(By.XPath(xpathOfElement));
var isSucceededed = element != null;
The best way I've seen is to utilize the stalenessOf ExpectedCondition, to wait for the old page to become stale.
Example:
WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement oldHtml = driver.findElement(By.tagName("html"));
wait.until(ExpectedConditions.stalenessOf(oldHtml));
It'll wait for ten seconds for the old HTML tag to become stale, and then throw an exception if it doesn't happen.
I use node + selenium-webdriver(which version is 3.5.0 now). what I do for this is:
var webdriver = require('selenium-webdriver'),
driver = new webdriver.Builder().forBrowser('chrome').build();
;
driver.wait(driver.executeScript("return document.readyState").then(state => {
return state === 'complete';
}))
You can use wait. there are basically 2 types of wait in selenium
Implicit wait
Explicit wait
- Implicit wait
This is very simple please see syntax below:
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
- Explicit wait
Explicitly wait or conditional wait in this wait until given condition is occurred.
WebDriverWait wait = new WebDriverWait(driver, 40);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
You can use other properties like visblityOf(), visblityOfElement()
If someone uses selenide:
public static final Long SHORT_WAIT = 5000L; // 5 seconds
$("some_css_selector").waitUntil(Condition.appear, SHORT_WAIT);
More Conditions can be found here:
http://selenide.org/javadoc/3.0/com/codeborne/selenide/Condition.html
In my case , I used the following to know the page load status. In our application loading gif(s) are present and, I listen to them as follows to eliminate unwanted wait time in the script.
public static void processing(){
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[#id='Msgpanel']/div/div/img")));
}
Where the xpath locates the gif in the HTML DOM.
After this, You may also implement your action methods Click.
public static void click(WebElement elementToBeClicked){
WebDriverWait wait = new WebDriverWait(driver, 45);
wait.until(ExpectedConditions.visibilityOf(element));
wait.until(ExpectedConditions.elementToBeClickable(element));
wait.ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class); elementToBeClicked.click();
}