Dynamic Web Table : incorrect count of rows in table - java

I have code that reads the rows from a web table that consists of multiple pages and prints them. On the last page there are only 2 rows, but the code counts 10 instead (10 is the max rows on a page). When I run it in debug mode and step through, it correctly counts 2 rows. I have no idea why this is happening and would like some help on this.
Here is the web table : https://demo.opencart.com/admin/index.php?route=sale/order&user_token=cf5c87b778476447f8451877fae6af2f
Here is where the rows are counted in my code:
//get number of rows
int rows = driver.findElements(By.xpath("//table[#class='table table-bordered table-hover']//tbody/tr")).size();
System.out.println("No of Rows: "+rows);
Here is all my code:
package mypackage;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class DynamicWebTable {
static WebDriver driver;
public static void main(String[] args) throws InterruptedException {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("https://demo.opencart.com/admin/");
driver.manage().window().maximize();
WebElement username = driver.findElement(By.id("input-username"));
username.clear();
username.sendKeys("demo");
WebElement password = driver.findElement(By.id("input-password"));
password.clear();
password.sendKeys("demo");
driver.findElement(By.xpath("//button[normalize-space()='Login']")).click();
//Close popup
if(driver.findElement(By.xpath("//div[#class='modal-content']")).isDisplayed()) {
driver.findElement(By.xpath("//button[#class='btn-close']")).click();
}
driver.findElement(By.xpath("//a[normalize-space()='Sales']")).click();
driver.findElement(By.xpath("//a[normalize-space()='Orders']")).click();
//get total no of pages
String textWithTotalPages = driver.findElement(By.xpath("//div[#class='col-sm-6 text-end']")).getText();
int pages = getNumberOfPages(textWithTotalPages);
System.out.println(pages);
//go through each page
for(int p = 1; p <= pages; p++) {
System.out.println("Page no: "+p);
**//get number of rows
int rows = driver.findElements(By.xpath("//table[#class='table table-bordered table-hover']//tbody/tr")).size();
System.out.println("No of Rows: "+rows);**
//read rows from page
for(int r=1; r<=rows; r++) {
String orderId = retryingFindClick(By.xpath("//table[#class='table table-bordered table-hover']//tbody//tr["+r+"]//td[2]"));
String store = retryingFindClick(By.xpath("//table[#class='table table-bordered table-hover']//tbody//tr["+r+"]//td[3]"));
String customer = retryingFindClick(By.xpath("//table[#class='table table-bordered table-hover']//tbody//tr["+r+"]//td[4]"));
String status = retryingFindClick(By.xpath("//table[#class='table table-bordered table-hover']//tbody//tr[\"+r+\"]//td[5]"));
System.out.println(orderId+ " "+store+" "+customer+" "+status);
}
//stop when finished with the last page
if(p == pages) {
break;
}
//click next page
String nextPage = Integer.toString(p + 1);
// try {
// driver.findElement(By.xpath("//ul[#class='pagination']//li//a[text()='"+nextPage+"']")).click();
// }catch(ElementClickInterceptedException e) {}
JavascriptExecutor js= (JavascriptExecutor) driver;
js.executeScript("window.scrollTo(0,document.body.scrollHeight)");
Thread.sleep(2000);
driver.findElement(By.xpath("//ul[#class='pagination']//li//a[text()='"+nextPage+"']")).click();
//waitAndLocate(By.xpath("//ul[#class='pagination']//li//a[text()='"+nextPage+"']")).click();
System.out.println("Clicked page: "+nextPage);
}
driver.quit();
}
//extract number of pages from String
public static int getNumberOfPages(String text){
return Integer.valueOf(text.substring(text.indexOf("(")+1, text.indexOf("Pages")-1));
}
public static String retryingFindClick(By by) {
//boolean result = false;
String s = null;
int attempts = 0;
while(attempts < 2) {
try {
s = driver.findElement(by).getText();
//result = true;
break;
} catch(StaleElementReferenceException e) {
}
attempts++;
}
return s;
}
public static WebElement waitAndLocate(By by) {
return new WebDriverWait(driver, Duration.ofSeconds(2))
.until(driver -> driver.findElement(by));
}
}

I rewrote your code to simplify things.
I think your main issue was that the script was not waiting properly so when it printed the number of rows for the last page, it was actually printing them for the previous page. The way to prevent this is to use ExpectedConditions.stalenessOf(). This takes an existing element on the page and then waits for it to go stale (no longer on the page). I added this at the bottom of every loop to make sure when the > button is clicked to go to the next page, the script pauses until the next page is loaded.
I added waits, where needed
Instead of getting a count of pages, just loop until there's no > (Next) button
Instead of getting a count of rows, just loop through all rows
I changed the URL to navigate straight to the sale/order page to save time
I removed the implicit wait since you shouldn't mix implicit and explicit waits according to the docs
Removed all support methods since they were no longer needed
With all these changes, the code is much shorter, simpler, and works successfully.
public static void main(String[] args) throws InterruptedException {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().window().maximize();
String url = "https://demo.opencart.com/admin/index.php?route=sale/order";
driver.get(url);
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("input-username"))).sendKeys("demo");
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("input-password"))).sendKeys("demo");
wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("i.fa-key"))).click();
// loop until last page is reached
while (true) {
for (WebElement row : wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("table tr")))) {
String orderId = row.findElement(By.xpath("./td[2]")).getText();
String store = row.findElement(By.xpath("./td[3]")).getText();
String customer = row.findElement(By.xpath("./td[4]")).getText();
String status = row.findElement(By.xpath("./td[5]")).getText();
System.out.println(orderId + " " + store + " " + customer + " " + status);
}
List<WebElement> nextButton = driver.findElements(By.xpath("//a[text()='>']"));
if (nextButton.isEmpty()) {
break;
} else {
nextButton.get(0).click();
wait.until(ExpectedConditions.stalenessOf(nextButton.get(0)));
}
}
}

Related

Using Selenium Java I want to automate a table that contains a price list and a price calculator

Problem: Using Selenium Java I want to automate a table that contains a price list and a price calculator
Another little problem: I can't click on the "Analysis" (Filter) button, because I have to double-click, which I could do in this case
What I want: I'm trying to find a method so that when I run the program, the program will click on another analysis (on another + button) Now with the help of a selector, I can only click on the first (+). But I want each time, the program to test, randomly other analyzes (other buttons +).
What I do
#FindBy(xpath = "//*[#id='footable_501']/thead/tr[2]/th[1]")
WebElement analizaSort;
#FindBy(xpath = "//*[#id='footable_501']/thead/tr[2]/th[2]")
WebElement pretSort;
#FindBy(xpath = "//*[#id='calculator']/div[1]/div[2]/div[2]")
WebElement total;
public void checkCalculator()
{
add.click();
add2.click();
}
public void checkFilter()
{
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("window.scrollBy(0,500)"); //Scroll vertically down by 1000 pixels
analizaSort.click();
analizaSort.click();
pretSort.click();
}
Link: https://www.poliana.ro/analize-preturi/
I've added below the code to click on a random analyze and to calculate the total:
#FindBy(css = "th.footable-sortable[class*='pret']") // css locator is faster than xpath
WebElement pretSort;
#FindBy(css = "th.footable-sortable[class*='analiza']")
WebElement analizaSort;
#FindBy(css = "tr[class*='row'] td[class*='pret']")
List<WebElement> analyzePriceList;
#FindBy(css = "#calculator .total .right")
WebElement total;
public void checkCalculator() {
int elementListSize = analyzePriceList.size();
assertTrue("No analyze was found in the table", elementListSize != 0); // replace here with the specific of your testing framework
int elementIndex = getRandomNumber(elementListSize - 1);
scrollElementToTheMiddle(analyzePriceList.get(elementIndex));
int expectedTotal = getTextAsInt(analyzePriceList.get(elementIndex));
analyzePriceList.get(elementIndex).click();
String totalAsString = total.getText().replace("lei", "");
int actualTotal = getInt(totalAsString);
assertEquals(expectedTotal, actualTotal);
}
public void checkFilter() {
scrollElementToTheMiddle(analizaSort);
analizaSort.click();
analizaSort.click(); // if you need double click, please see the below method
pretSort.click();
}
private void doubleClick(WebElement element) {
Actions act = new Actions(driver);
act.doubleClick(element).build().perform();
}
private int getTextAsInt(WebElement element) {
String text = element.getText();
return getInt(text);
}
private int getInt(String text) {
assertTrue("The " + text + " text was expected to be numeric", isNumeric(text)); // replace here with the specific of your testing framework
return Integer.parseInt(text);
}
private boolean isNumeric(String possibleNumberAsString) {
Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?\n");
if (possibleNumberAsString == null) {
return false;
}
return pattern.matcher(possibleNumberAsString.trim()).matches();
}
private int getRandomNumber(int maximum) {
return ThreadLocalRandom.current().nextInt(0, maximum);
}
private void scrollElementToTheMiddle(WebElement element) {
String scrollElementIntoMiddle = "var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);"
+ "var elementTop = arguments[0].getBoundingClientRect().top;"
+ "window.scrollBy(0, elementTop-(viewPortHeight/2));";
((JavascriptExecutor) driver).executeScript(scrollElementIntoMiddle, element);
}

Is there any way to solve sorting web element ? I am getting difficulties while sorting using drag and drop function

Is there any way to solve sorting web element ? I am getting difficulties while sorting using drag and drop function. My drag and drop is not working, i think my logic is good, but while running code nothing happening...
public void sortable() { // loop for drag and drop is not working...
try {
driver.get("http://jqueryui.com/");
myLibrary.clickButton(By.partialLinkText("Sortable"));
WebElement iframe = driver.findElement(By.xpath("//*[#id='content']/iframe"));
driver.switchTo().frame(iframe);
String temp = "";
Thread.sleep(10 * 1000); //manual work to disorder sortable list prior to start for loop.
Actions action = new Actions(driver);
int i = 1, j = 1;
for (i = 1; i < 8; i = i + 1) {
WebElement sourceText = driver.findElement(By.cssSelector("#sortable > li:nth-child(" + i + ")"));
WebElement dragElement = driver
.findElement(By.cssSelector("#sortable > li:nth-child(" + i + ") > span"));
while (true) {
temp = "Item" + " " + j;
if (temp == sourceText.getText()) {
WebElement targetElement = driver
.findElement(By.cssSelector("#sortable > li:nth-child(" + j + ")"));
action.moveToElement(dragElement).dragAndDrop(dragElement, targetElement).build().perform();
Thread.sleep(1 * 1000);
break;
} else {
if (j == 8) {
break;
} else {
j++;
}
}
}
}
Thread.sleep(5 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
Building upon what #JeffC posted as an answer, here's another variant of the same that uses some of the built in capabilities of Java.
We basically use the List.sort() and a Comparator to get this done.
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class SorterSample {
private RemoteWebDriver driver;
#BeforeClass
public void setup() {
driver = new ChromeDriver();
}
#AfterClass
public void cleanup() {
if (driver != null) {
driver.quit();
}
}
#Test
public void testMethod() {
String url = "http://jqueryui.com/sortable/";
driver.get(url);
driver.switchTo().frame(driver.findElement(By.cssSelector("iframe.demo-frame")));
List<WebElement> items = driver.findElements(By.cssSelector("#sortable > li"));
items.sort(
(o1, o2) -> {
int compareValue = o1.getText().compareTo(o2.getText());
if (compareValue < 0) {
new Actions(driver).dragAndDrop(o1, o2).perform();
}
return compareValue;
});
}
}
This was a fun little exercise...
Rather than trying to invent your own sorting code, you should use an established sorting algorithm. I chose bubble sort but you can pick any one you want. If you are just sorting a few items, this should work just fine. It runs in just a few seconds.
The main code
String url = "http://jqueryui.com/sortable/";
driver.get(url);
driver.switchTo().frame(driver.findElement(By.cssSelector("iframe.demo-frame")));
List<WebElement> items = driver.findElements(By.cssSelector("#sortable > li"));
bubbleSort(items);
The bubble sort method
static void bubbleSort(List<WebElement> items)
{
int n = items.size();
boolean swapped;
for (int i = 0; i < n - 1; i++)
{
swapped = false;
for (int j = 0; j < n - i - 1; j++)
{
int compare = items.get(j).getText().compareTo(items.get(j + 1).getText());
if (compare < 0)
{
swap(items.get(j + 1), items.get(j));
swapped = true;
}
}
// break if no elements were swapped
if (swapped == false)
{
break;
}
}
}
and finally the support method to swap items
public static void swap(WebElement source, WebElement target)
{
new Actions(driver).dragAndDrop(source, target).perform();
}
I just ran this code a few times and it's working just fine. It sorts the items in reverse order (so you don't have to mix them up manually). This is not the most efficient way to do this but I wanted to be able to watch the bubble sort work so you see each swap. If you want this to go faster, you can pull the text from the items, sort that list and then line up the elements with their sorted text so you only have to do n swaps.

How to solve net.thucydides.core.webdriver.exceptions.ElementShouldBeVisibleException- selenium

I am automating a page where I must choose two prices, when choosing the first price the page goes down the scroll and that's where the test fails, I get the error, I've already tried with explicit waits and thread.sleep but it does not work. any ideas?
this is the error:
net.thucydides.core.webdriver.exceptions.ElementShouldBeVisibleException: Expected condition failed: waiting for [[RemoteWebDriver: chrome on XP (e64b73bc32012e0506c1b2816cbc2ac2)] -> xpath: //div[#id='divAvailabilityOut']//span[#data-bind='html: Price']] to be displayed (tried for 50 second(s) with 100 milliseconds interval)
and this is my code:
public void escogerPreioMasCaro() throws InterruptedException {
List<WebElementFacade> listPreciosCaros = findAll(
"//div[#class='availabilityIn box']//span[#data-bind='html: Price']");
String[] strVectorCaros = new String[listPreciosCaros.size()];
int i = 0;
for (WebElementFacade listado : listPreciosCaros) {
System.out.println("Lista Precios Caros" + listado.getText());
strVectorCaros[i] = listado.getText().replaceAll("COP", " ").trim();
i++;
}
System.out.println(Arrays.toString(strVectorCaros).trim());
double[] vec1 = new double[strVectorCaros.length];
for (int g = 0; g < strVectorCaros.length; g++) {
try {
vec1[g] = Double.parseDouble(strVectorCaros[g]);
} catch (NumberFormatException ex) {
System.err.println("Ilegal input");
}
}
// System.out.println(Arrays.toString(vec1));
double precioMayor = vec1[0];
for (int x = 0; x < vec1.length; x++) {
// System.out.println(nombres[i] + " " + sueldos[i]);
if (vec1[x] > precioMayor) { //
precioMayor = vec1[x];
}
}
System.out.println("PrecioCaro" + precioMayor);
String precioMayorString = String.valueOf(precioMayor);
System.out.println("string " + precioMayorString);
for (WebElementFacade listado2 : listPreciosCaros) {
if (listado2.getText().contains(precioMayorString)) {
System.out.println(precioMayorString);
listado2.click();
}
}
Thread.sleep(2000);
}
What I am doing is to go through the series of prices and separate them to choose only the number, then pass them to double type vector to be able to compare their prices and choose the most expensive one
this is the page of the automation, the error presents it after choosing the destination and dates of travel
https://www.vivaair.com/co/flight/reserva
As I understood you try to find and select ticket with max price.
Here code sample to find and select max price for From and To figths:
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
List<Double> pricesDouble = new ArrayList<>();
//Get all prices without currency of FROM Flight section
List<WebElement> fromPrices = driver.findElements(By.cssSelector("#divAvailabilityOut span[data-bind='html: PriceWithoutCurrencySymbol']"));
//To get all prices without currency of TO Flight section use code below
//List<WebElement> toPrices = driver.findElements(By.cssSelector("#availabilityLoadingIn span[data-bind='html: PriceWithoutCurrencySymbol']"));
fromPrices.forEach(e -> {
try {
pricesDouble.add(Double.parseDouble(e.getAttribute("innerText")));
} catch (NumberFormatException ex) {
System.err.println("Ilegal input");
}
});
//Assert.assertTrue(pricesDouble.size()>0, "Get at least one price");
int indexOfMaxPrice = pricesDouble.indexOf(Collections.max(pricesDouble););
//Get clickable element with max price
fromPrices.get(indexOfMaxPrice).findElement(By.xpath("ancestor::div[#class='totalPrice']")).click();
Here helpful links:
https://www.w3schools.com/cssref/css_selectors.asp
https://www.w3schools.com/xml/xpath_intro.asp

How to handle pagination in selenium Webdriver

This is my code when I run it goes to the 2nd page and stop it not looping all pages and search the required element. It should look for particular text in a page and click on the link of it.
if (driver.getTitle().contains("Article properties")) {
WebElement assigninitial = driver.findElement(By.partialLinkText("Assign reviewer for initial review"));
assigninitial.click();
} else {
//pagination code
//driver.findElement(By.xpath("/html/body/app/main/app-mytasks/div[1]/section/div/pagination/ul/li[3]/a")).click();
#SuppressWarnings("unchecked")
ArrayList<WebElement> pagination = (ArrayList<WebElement>) driver.findElements(By.xpath("/html/body/app/main/app-mytasks/div[1]/section/div/pagination/ul/li[5]/a"));
// checkif pagination link exists
if ((pagination).size() > 0) {
//System.out.print("pagination exists");
// click on pagination link
for (int i = 0; i < pagination.size(); i++) {
pagination.get(i).click();
if (driver.getPageSource().contains("Article properties")) {
WebElement assigninitial = driver.findElement(By.partialLinkText("Assign reviewer for initial review"));
assigninitial.click();
}
}
} else {
System.out.print("pagination not exists");
}
}
public void pagination_check() throws InterruptedException {
loader_wait(5); //wait until 'loader' loading
List<WebElement> pagination = driver.findElements(By.xpath("//page-navigation/div/div/span/a"));
Thread.sleep(5000);
if (pagination.size() > 0) {
System.out.println("pagination exists and size=>"+pagination.size());
int page_no=pagination.size();
for (int i = 2; i <= pagination.size(); i++) {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].scrollIntoView();", driver.findElement(By.xpath("//page-navigation/div/div/span"))); //for
scroller move
js.executeScript("arguments[0].click();", driver.findElement(By.xpath("//page-navigation/div/div/span/a["+i+"]")));
loader_wait(5); //wait
}
} else {
System.out.println("no pagination");
}
}

navigate between pages Google Search with selenium - Java

How do I search for something on Google , click on the link , then return to the search and click the following link ( without repeating the already links clicked ) and after the links that the page finished , navigate to the page 2 and repeat the same steps in succession?
So far I could go to the first link, go to the page and return to the list of links searched only.
package Search;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class GoogleSearch {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
System.setProperty("webdriver.ie.driver", "C:/Users/paulo.roberto/Documents/eclipse/Selenium/IEDriverServer.exe");
DesiredCapabilities caps = DesiredCapabilities.internetExplorer();
caps.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
WebDriver driver = new InternetExplorerDriver(caps);
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.get("https://www.google.com.br");
driver.findElement(By.name("q")).sendKeys("test");
driver.findElement(By.name("btnG")).click();
WebElement myDynamicElement = (new WebDriverWait(driver, 10)).until(ExpectedConditions.presenceOfElementLocated(By.id("resultStats")));
// find the number of pages
int size = driver.findElements(By.cssSelector("[valign='top'] > td")).size();
for(int j = 1 ; j < size ; j++) {
if (j > 1) {// we don't need to navigate to the first page
driver.findElement(By.cssSelector("[aria-label='Page " + j + "']")).click(); // navigate to page number j
}
String pagesearch = driver.getCurrentUrl();
List<WebElement> findElements = driver.findElements(By.xpath("//*[#id='rso']//h3/a"));
System.out.println(findElements.size());
for(int i=0;i<findElements.size();i++){
findElements= driver.findElements(By.xpath("//*[#id='rso']//h3/a"));
findElements.get(i).click();
driver.navigate().to(pagesearch);
// or driver.navigate().back();
}
}
}
}
message on the console:
Started InternetExplorerDriver server (32-bit)
2.48.0.0
Listening on port 37101
10
10
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 4, Size: 0
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at Search.GoogleSearch.main(GoogleSearch.java:48)
You can use nested loops. One to navigate between results pages and one to click on results
// find the number of pages
int size = driver.findElements(By.cssSelector("[valign='top'] > td")).size();
for(int j = 1 ; j < size ; j++) {
if (j > 1) {// we don't need to navigate to the first page
driver.findElement(By.cssSelector("[aria-label='Page " + j + "']")).click(); // navigate to page number j
}
String pagesearch = driver.getCurrentUrl();
List<WebElement> findElements = driver.findElements(By.xpath("//*[#id='rso']//h3/a"));
System.out.println(findElements.size());
for(int i=0;i<findElements.size();i++){
findElements= driver.findElements(By.xpath("//*[#id='rso']//h3/a"));
findElements.get(i).click();
driver.navigate().to(pagesearch);
// or driver.navigate().back();
}
}

Categories