How to wait for a certain number of rows? - java

I'm testing a webpage and some of its content is loaded with XMLHttpRequest. I need to check if my <table> contains 2 rows (because the page already contains 1 row when loading) after the Ajax call.
My test:
#Before
public void setUp() throws Exception {
driver = new FirefoxDriver();
driver.get("http://localhost/index.html");
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
}
#Test
public void testIfPartnerListPageIsPresent() {
driver.findElement(By.id("123")).click();
List<WebElement> rawList = driver.findElement(By.id("id-213"))
.findElements(By.tagName("tr"));
assertTrue("More than 1 raw", rawList.size() > 1);
}
How can I ask to Selenium to wait for other rows in my table?

This function will wait until the table contains at least given number of rows
public void waitUntilRowPopulates(WebElement element, final int rowCount) {
final WebElement table = element;
new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(10, TimeUnit.MILLISECONDS)
.until(new Predicate<WebDriver>() {
public boolean apply(WebDriver d) {
List<WebElement> rawList = table.findElements(By.tagName("tr"));
return (rawList.size() >= rowCount);
}
});
}

Following piece of code should help:
int noOfRowsBeforeAJAXCall = driver.findElement(By.id("id-213")).findElements(By.tagName("tr")).size();
int noOfRowsAfterAJAXCall;
driver.findElement(By.id("123")).click();
while(1)
{
noOfRowsAfterAJAXCall = driver.findElement(By.id("id-213")).findElements(By.tagName("tr")).size();
// Check whether row nos. increased
if(noOfRowsAfterAJAXCall>noOfRowsBeforeAJAXCall)
break;
// Re-check after 1 sec
Thread.sleep(1000m);
}

For web pages, especially ones that have a lot of content inside, it is advisable to use JSoup library to grab the html, parse inside. This can also be used for the purpose of synchronization.
Sample code:
String pageSource = driver.getPageSource();
Document doc = Jsoup.parse(pageSource);
String someValue = doc.getElementsByAttributeValue("id", "specificValue");
JSoup has a lot of getter methods that allow flexibility in finding what we want. It supports CSS query as well.

Related

Not able to access all the webelements from the grid in selenium

In webpage i have multiple grids like below
As you can see not all the web elements are in view. We need to scroll to see other elements.
This grid have around 20 rows and 10 columns. Number of rows will change as per scenario.
My requirement is to fetch data of all the web elements of this grid
I have written web elements as below
#FindBy(xpath = "xpath here")
private static List<WebElement> sampleGridDate; (This should return all web elements from date column)
#FindBy(xpath = "xpath here")
private static List<WebElement> sampleGridCode; (This should return all web elements from code column)
I am fetching data with below code
for (int i = 0; i < sampleGridDate.size(); i++) {
scrollToViewElement(driver,sampleGridDate.get(i));
row.setDate(sampleGridDate.get(i).getText());
scrollToViewElement(driver,sampleGridCode.get(i));
row.setCode(sampleGridCode.get(i).getText());
.....so on
}
public static void scrollToViewElement(WebDriver driver, WebElement element) {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].scrollIntoView();", element);
}
**
Problems i am facing are
sampleGridDate.size() returns 16 instead of 20
this for loop runs only once as sampleGridDate is not found after first iteration (stale webelement exception)
**
How do i fix this

Selenium WebDriverWait doesn't return all web elements within classes with same name

Im using this method to retrieve all src from div classes. For example in the page from my code there are 5 elements, but when I run this code I get only 2 src. And if I run my code multiple times, sometimes it returns all of 5 elements.
public static void main(String[] args) throws IOException, URISyntaxException {
System.setProperty("webdriver.chrome.driver", "S:\\behance-id\\src\\main\\resources\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("https://www.behance.net/gallery/148589707/Hercules-and-Randy");
List<WebElement> firstResult = new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath("//div[#class='ImageElement-root-kir ImageElement-loaded-icR']/img")));
for (WebElement webElement : firstResult) {
System.out.println(webElement.getAttribute("src"));
}
driver.quit();
}
Also tried to add this line, but it didn't help:
((JavascriptExecutor)driver).executeScript("window.scrollTo(0, document.body.scrollHeight)");
So this script doesn't return all of needed elements even though they are the same class.
Page source code looks like:
ExpectedCondition<java.util.List<WebElement>> presenceOfAllElementsLocatedBy(By locator)
Above is for checking that there is at least one element present on a web page and will break as soon as it find one, it will not wait for 5 elements.It returns true as soon as elements.size>0
You need to add to some other wait to make sure all elements are loaded before you do findelements
Below will wait for count of elements be 5
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
int elementCount = driver.findElements(By.xpath("xxxx")).size();
if (elementCount == 5)
return true;
else
return false;
}
});

Is it possible to refresh Select objects in Selenium when testing ajax

When I was working on my selenium tests, I ran into an issue when I was testing some ajax functionality on a website. I was getting an error Exception in thread "main" org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM.
After looking up a bunch of stuff, I know the reason that I am getting this error is because the element that I am accessing in my first select object is considered since the ajax reloaded that section of the site.
In order to get around this exception, I just created a new select object each time. The xpath does not change when the page is reloaded.
Is it possible to just refresh the Select with the new xpath to the object, instead of creating a new one each time?
Thanks for the help.
public static boolean ajaxFunctionalityFF() throws InterruptedException {
int rowCount=driver.findElements(By.xpath("//table[#class='classname']/tbody/tr")).size();
rowSizes.add(rowCount);
Select ajaxSelector = new Select(driver.findElement(By.id("edit-term")));
ajaxSelector.selectByVisibleText("-Beef);
Thread.sleep(4000);
rowCount=driver.findElements(By.xpath("//table[#class='classname']/tbody/tr")).size();
rowSizes.add(rowCount);
totalElements = totalElements + rowCount;
Select ajaxSelector2 = new Select(driver.findElement(By.id("edit-term"))); //create a new one to fix the stale element exception
ajaxSelector2.selectByVisibleText("-Cattle");
Thread.sleep(4000);
rowCount=driver.findElements(By.xpath("//table[#class='classname']/tbody/tr")).size();
You will need to fetch it each time that section of the HTML is refreshed. I would do something like
private By selectLocator = By.id("edit-term");
public static boolean ajaxFunctionalityFF() throws InterruptedException
{
...
Select ajaxSelector = getSelect();
...
ajaxSelector = getSelect();
ajaxSelector.selectByVisibleText("-Cattle");
...
}
public static Select getSelect()
{
return new Select(driver.findElement(selectLocator));
}
One workaround that i usually use for such cases is as below:
do
{
try
{
WebElement element=FindThatElement;
element.performSomeAction();
break;
}
catch(StaleElementException | //Any Other unExpectedException e)
{
//continue do while loop;
}
} while(1>0);

wait.until(ExpectedConditions.visibilityOf Element1 OR Element2)

I want use wait.until(ExpectedConditions) with TWO elements.
I am running a test, and I need WebDriver to wait until either of Element1 OR Element2 shows up. Then I need to pick whoever shows up first. I've tried:
WebDriverWait wait = new WebDriverWait(driver, 60);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h2[#class='....']"))) || wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h3[#class='... ']")));
// Then I would need:
String result = driver.findElement(By.xpath("...")).getText() || driver.findElement(By.xpath("...")).getText();
To sum up, I need to wait until either of the TWO elements shows up. Then pick whoever shows up (they cannot show up simultaneously)
Please Help.
Now there's a native solution for that, the or method, check the doc.
You use it like so:
driverWait.until(ExpectedConditions.or(
ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.something")),
ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.anything"))));
This is the method I declared in my Helper class, it works like a charm. Just create your own ExpectedCondition and make it return any of elements found by locators:
public static ExpectedCondition<WebElement> oneOfElementsLocatedVisible(By... args)
{
final List<By> byes = Arrays.asList(args);
return new ExpectedCondition<WebElement>()
{
#Override
public Boolean apply(WebDriver driver)
{
for (By by : byes)
{
WebElement el;
try {el = driver.findElement(by);} catch (Exception r) {continue;}
if (el.isDisplayed()) return el;
}
return false;
}
};
}
And then you can use it this way:
Wait wait = new WebDriverWait(driver, Timeouts.WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
WebElement webElement = (WebElement) wait.until(
Helper.oneOfElementsLocatedVisible(
By.xpath(SERVICE_TITLE_LOCATOR),
By.xpath(ATTENTION_REQUEST_ALREADY_PRESENTS_WINDOW_LOCATOR)
)
);
Here SERVICE_TITLE_LOCATOR and ATTENTION_REQUEST_ALREADY_PRESENTS_WINDOW_LOCATOR are two static locators for page.
I think that your problem has a simple solution if you put "OR" into xpath.
WebDriverWait wait = new WebDriverWait(driver, 60);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h2[#class='....'] | //h3[#class='... ']")));
Then, to print the result use for example:
if(driver.findElements(By.xpath("//h2[#class='....']")).size()>0){
driver.findElement(By.xpath("//h2[#class='....']")).getText();
}else{
driver.findElement(By.xpath("//h3[#class='....']")).getText();
}
You can also use a CSSSelector like this:
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("h2.someClass, h3.otherClass")));
Replace someClass and otherClass with what you had in [...] in the xpath.
Unfortunately, there is no such a command. You can overcome this by try and catch, or I would better recommend you to use open source Ruby library Watir.
There is an alternative way to wait but it isnt using expected conditions and it uses a lambda expression instead..
wait.Until(x => driver.FindElements(By.Xpath("//h3[#class='... ']")).Count > 0 || driver.FindElements(By.Xpath("//h2[#class='... ']")).Count > 0);
If you have a variable number of conditions to satisfy, you can leverage a generic list such as Java's ArrayList<> as shown here, and then do the following:
//My example is waiting for any one of a list of URLs (code is java)
public void waitUntilUrls(List<String> urls) {
if(null != urls && urls.size() > 0) {
System.out.println("Waiting at " + _driver.getCurrentUrl() + " for " + urls.size() + " urls");
List<ExpectedCondition<Boolean>> conditions = new ArrayList<>();
for (String url : urls) {
conditions.add(ExpectedConditions.urlContains(url));
}
WebDriverWait wait = new WebDriverWait(_driver, 30);
ExpectedCondition<Boolean>[] x = new ExpectedCondition[conditions.size()];
x = conditions.toArray(x);
wait.until(ExpectedConditions.or(x)); // "OR" selects from among your ExpectedCondition<Boolean> array
}
}
wait.until(
ExpectedConditions.or(
ExpectedConditions.elementToBeClickable(By.xpath("yourXpath"),
ExpectedConditions.elementToBeClickable(By.xpath("yourAnotherXpath")
)
);
There is a simple solution for this, using an Explicit Wait:
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[#id='FirstElement' or #id='SecondElement']")));
Before this I was trying to use wait.until(ExpectedConditions.or(..., which was not compatible with selenium versions before 2.53.0.

Test if an element is present using Selenium WebDriver

Is there a way how to test if an element is present? Any findElement method would end in an exception, but that is not what I want, because it can be that an element is not present and that is okay. That is not a fail of the test, so an exception can not be the solution.
I've found this post: Selenium C# WebDriver: Wait until element is present.
But this is for C#, and I am not very good at it. What would the code be in Java? I tried it out in Eclipse, but I didn't get it right into Java code.
This is the code:
public static class WebDriverExtensions{
public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutInSeconds){
if (timeoutInSeconds > 0){
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
return wait.Until(drv => drv.FindElement(by));
}
return driver.FindElement(by);
}
}
Use findElements instead of findElement.
findElements will return an empty list if no matching elements are found instead of an exception.
To check that an element is present, you could try this
Boolean isPresent = driver.findElements(By.yourLocator).size() > 0
This will return true if at least one element is found and false if it does not exist.
The official documentation recommends this method:
findElement should not be used to look for non-present elements, use findElements(By) and assert zero length response instead.
Use a private method that simply looks for the element and determines if it is present like this:
private boolean existsElement(String id) {
try {
driver.findElement(By.id(id));
} catch (NoSuchElementException e) {
return false;
}
return true;
}
This would be quite easy and does the job.
You could even go further and take a By elementLocator as a parameter, eliminating problems if you want to find the element by something other than an id.
I found that this works for Java:
WebDriverWait waiter = new WebDriverWait(driver, 5000);
waiter.until( ExpectedConditions.presenceOfElementLocated(by) );
driver.FindElement(by);
public static WebElement FindElement(WebDriver driver, By by, int timeoutInSeconds)
{
WebDriverWait wait = new WebDriverWait(driver, timeoutInSeconds);
wait.until( ExpectedConditions.presenceOfElementLocated(by) ); //throws a timeout exception if element not present after waiting <timeoutInSeconds> seconds
return driver.findElement(by);
}
I had the same issue. For me, depending on a user's permission level, some links, buttons and other elements will not show on the page. Part of my suite was testing that the elements that should be missing, are missing. I spent hours trying to figure this out. I finally found the perfect solution.
It tells the browser to look for any and all elements based specified. If it results in 0, that means no elements based on the specification was found. Then I have the code execute an *if statement to let me know it was not found.
This is in C#, so translations would need to be done to Java. But it shouldn’t be too hard.
public void verifyPermission(string link)
{
IList<IWebElement> adminPermissions = driver.FindElements(By.CssSelector(link));
if (adminPermissions.Count == 0)
{
Console.WriteLine("User's permission properly hidden");
}
}
There's also another path you can take depending on what you need for your test.
The following snippet is checking to see if a very specific element exists on the page. Depending on the element's existence I have the test execute an if else.
If the element exists and is displayed on the page, I have console.write let me know and move on. If the element in question exists, I cannot execute the test I needed, which is the main reasoning behind needing to set this up.
If the element does not exist and is not displayed on the page, I have the else in the if else execute the test.
IList<IWebElement> deviceNotFound = driver.FindElements(By.CssSelector("CSS LINK GOES HERE"));
// If the element specified above results in more than 0 elements and is displayed on page execute the following, otherwise execute what’s in the else statement
if (deviceNotFound.Count > 0 && deviceNotFound[0].Displayed){
// Script to execute if element is found
} else {
// Test script goes here.
}
I know I'm a little late on the response to the OP. Hopefully this helps someone!
Try this:
Call this method and pass three arguments:
WebDriver variable. Assuming driver_variable as the driver.
The element which you are going to check. It should provide a from By method. Example: By.id("id")
Time limit in seconds.
Example: waitForElementPresent(driver, By.id("id"), 10);
public static WebElement waitForElementPresent(WebDriver driver, final By by, int timeOutInSeconds) {
WebElement element;
try{
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS); // Nullify implicitlyWait()
WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
element = wait.until(ExpectedConditions.presenceOfElementLocated(by));
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); // Reset implicitlyWait
return element; // Return the element
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
This works for me:
if(!driver.findElements(By.xpath("//*[#id='submit']")).isEmpty()){
// Then click on the submit button
}
else{
// Do something else as submit button is not there
}
You can make the code run faster by shorting the Selenium timeout before your try-catch statement.
I use the following code to check if an element is present.
protected boolean isElementPresent(By selector) {
selenium.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
logger.debug("Is element present"+selector);
boolean returnVal = true;
try{
selenium.findElement(selector);
} catch (NoSuchElementException e) {
returnVal = false;
} finally {
selenium.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
}
return returnVal;
}
Write the following function/methods using Java:
protected boolean isElementPresent(By by){
try{
driver.findElement(by);
return true;
}
catch(NoSuchElementException e){
return false;
}
}
Call the method with the appropriate parameter during the assertion.
If you are using rspec-Webdriver in Ruby, you can use this script, assuming that an element should really not be present, and it is a passed test.
First, write this method first from your class RB file:
class Test
def element_present?
begin
browser.find_element(:name, "this_element_id".displayed?
rescue Selenium::WebDriver::Error::NoSuchElementError
puts "this element should not be present"
end
end
Then, in your spec file, call that method.
before(:all) do
#Test= Test.new(#browser)
end
#Test.element_present?.should == nil
If your element is not present, your spec will pass, but if the element is present, it will throw an error, and the test failed.
Personally, I always go for a mixture of the above answers and create a reusable static utility method that uses the size() > 0 suggestion:
public Class Utility {
...
public static boolean isElementExist(WebDriver driver, By by) {
return driver.findElements(by).size() > 0;
...
}
This is neat, reusable, maintainable, etc.—all that good stuff ;-)
public boolean isElementDisplayed() {
return !driver.findElements(By.xpath("...")).isEmpty();
}
This should do it:
try {
driver.findElement(By.id(id));
} catch (NoSuchElementException e) {
//do what you need here if you were expecting
//the element wouldn't exist
}
I would use something like (with Scala [the code in old "good" Java 8 may be similar to this]):
object SeleniumFacade {
def getElement(bySelector: By, maybeParent: Option[WebElement] = None, withIndex: Int = 0)(implicit driver: RemoteWebDriver): Option[WebElement] = {
val elements = maybeParent match {
case Some(parent) => parent.findElements(bySelector).asScala
case None => driver.findElements(bySelector).asScala
}
if (elements.nonEmpty) {
Try { Some(elements(withIndex)) } getOrElse None
} else None
}
...
}
so then,
val maybeHeaderLink = SeleniumFacade getElement(By.xpath(".//a"), Some(someParentElement))
The simplest way I found in Java was:
List<WebElement> linkSearch= driver.findElements(By.id("linkTag"));
int checkLink = linkSearch.size();
if(checkLink!=0) {
// Do something you want
}
To find if a particular Element is present or not, we have to use the findElements() method instead of findElement()...
int i = driver.findElements(By.xpath(".......")).size();
if(i=0)
System.out.println("Element is not present");
else
System.out.println("Element is present");
This is worked for me...
You can try implicit wait:
WebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
driver.Url = "http://somedomain/url_that_delays_loading";
IWebElement myDynamicElement = driver.FindElement(By.Id("someDynamicElement"));
Or you can try explicit wait one:
IWebDriver driver = new FirefoxDriver();
driver.Url = "http://somedomain/url_that_delays_loading";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(By.Id("someDynamicElement"));
});
Explicit will check if the element is present before some action. Implicit wait could be called in every place in the code. For example, after some Ajax actions.
More you can find at SeleniumHQ page.
I am giving my snippet of code. So, the below method checks if a random web element 'Create New Application' button exists on a page or not. Note that I have used the wait period as 0 seconds.
public boolean isCreateNewApplicationButtonVisible(){
WebDriverWait zeroWait = new WebDriverWait(driver, 0);
ExpectedCondition<WebElement> c = ExpectedConditions.presenceOfElementLocated(By.xpath("//input[#value='Create New Application']"));
try {
zeroWait.until(c);
logger.debug("Create New Application button is visible");
return true;
} catch (TimeoutException e) {
logger.debug("Create New Application button is not visible");
return false;
}
}
In 2022 this can now be done without an annoying delay, or affecting your current implicit wait value.
First bump your Selenium driver to latest (currently 4.1.2).
Then you can use getImplicitWaitTimeout then set timeout to 0 to avoid a wait then restore your previous implicit wait value whatever it was:
Duration implicitWait = driver.manage().timeouts().getImplicitWaitTimeout();
driver.manage().timeouts().implicitlyWait(Duration.ofMillis(0));
final List<WebElement> signOut = driver.findElements(By.linkText("Sign Out"));
driver.manage().timeouts().implicitlyWait(implicitWait); // Restore implicit wait to previous value
if (!signOut.isEmpty()) {
....
}
Try the below code using the isDispplayed() method to verify if the element is present or not:
WebElement element = driver.findElements(By.xpath(""));
element.isDispplayed();
There could be multiple reasons due to which you might observe exceptions while locating a WebElement using Selenium driver.
I would suggest you to apply the below suggestions for different scenarios:
Scenario 1: You just want to find out if a certain WebElement is present on the screen or not. For example, the Save button icon will only appear until the form is fully filled and you may want to check if Save button is present or not in your test.
Use the below code -
public Boolean isElementLoaded(By locator){
return !getWebElements(this.driver.findElements(locator), locator).isEmpty();
}
Scenario 2: You want to wait before a WebElement becomes visible in the UI
public List<WebElement> waitForElementsToAppear(By locator) {
return wait.until(visibilityOfAllElementsLocatedBy(by));
}
Scenario 3: Your test is flaky because the WebElement becomes stale sometimes and gets detached from the DOM.
protected final List<Class<? extends WebDriverException>> exceptionList =
List.of(NoSuchWindowException.class,
NoSuchFrameException.class,
NoAlertPresentException.class,
InvalidSelectorException.class,
ElementNotVisibleException.class,
ElementNotSelectableException.class,
TimeoutException.class,
NoSuchSessionException.class,
StaleElementReferenceException.class);
public WebElement reactivateWebElement(By by, WebElement element){
try {
wait.ignoreAll(exceptionList)
.until(refreshed(visibilityOf(element)));
logger.info(("Element is available.").concat(BLANK).concat(element.toString()));
} catch (WebDriverException exception) {
logger.warn(exception.getMessage());
} return this.driver.findElement(by);
}

Categories