CP-SAT Blog Series – WRAPPER METHODS AND DYNAMIC TABLES

Problem Statement: Dynamic Tables, wait , + Stability and Reusability

Most of the time when handling dynamic data tables, tables where the rows and columns can be dynamically populated based on some search queries or any other calculations there is a need to read a particular column value and then act based on the result. There are two challenges here

  1. As table rows and columns get populated dynamically we need to handle them by using some explicit wait conditions
  2. Over a period of time similar situations might arise in other pages or similar projects, how to start putting these functions into a common utility or wrapper method classes to increase stability and re-usability

Problem case study

The case study is taken from the following website

https://datatables.net/examples/plug-ins/range_filtering.html

The page shows the following table.

 

On entering the minimum and maximum age it shows the data as per the age range. Or when the column is clicked it sorts the data.

So let us define two test scenarios from this case study.

  1. Check whether the age range is working fine or not (Use age range 37-40)
  2. Check whether the sorting is working fine or not (Sort on age column and see if it working or not)

 

Solution

Let us start building our solution.

Step 1 – Locator strategy

Few things to start with

  • We are using Google Chrome as the choice of browser and its DOM inspector to locate our elements.
  • We have opened the website in chrome and used the right click ->inspect on the age first column value and then copied the xpath and used ctrl-f to check the xpath in the device tool bar on chrome.
  • The resulting image looks like below and the xpath provided by chrome browser is //*[@id=”example”]/tbody/tr[1]/td[4]

 

The DOM structure looks like below

 

 

The tbody/tr/td is there, table has rows and columns.

If we want to fetch all the columns, we will be using the following xpath

//*[@id=”example”]/tbody/tr/td[4]

Or we could have also used below

//*[@id=”example”]/tbody/tr[*]/td[4]

Both these xpaths are able to fetch all the column 4 values.

Please look at the below screen shot, it shows that instead of single element the xpath is able to locate all the 10 column values for the 4th column.

 

We would also need locators for the age range input elements.

  1. A) //*[@id=”min”]
  2. B) //*[@id=”max”]

Step 2 – wait strategy and part A of our solution code

If we only have the locator and if we were to use findElement with this locator it might fail until we let the table populate with the new elements when the age range script comes into action.

One way is to use Thread.sleep() but it is not a recommended way for automation. We do not ever want to use hard coded waits. We would rather use an explicit wait with the condition that all the elements for the columns are visible, once that happens we will be able to fetch the relevant values.

Following lines of code will do the magic for us.

DynamicDataTablePartA.java

WebDriverWait wait = new WebDriverWait(driver,30);

By byFourthColumn = By.xpath(“//*[@id=\”example\”]/tbody/tr/td[4]”);

 

wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(byFourthColumn));

 

List<WebElement> listOfAllFourthColumnElements =

driver.findElements(byFourthColumn);

 

int size = listOfAllFourthColumnElements.size();

 

//get all the values of the new range of age

 

for (int i=0;i<size;i++) {

 

WebElement individualColumn = listOfAllFourthColumnElements.get(i);

System.out.println(individualColumn.getText());

}

 

The full codebase with part A of the solution can be taken from the following dropboxfolder for the file.

https://www.dropbox.com/s/igtma1l1jx0tq0g/DynamicDataTablePartA.java?dl=0

 

Step 3 – Extending the code to check if the age range is proper or not

We could get the list of columns and the values of the column printed on console in the previous code. We need to check whether the values are within the range or not.

We will extract the integer values and check whether it is in range or not.

Following piece of code will help us check

DynamicDataTablePartB.java

wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(byFourthColumn));

List<WebElement> listOfAllFourthColumnElements =

driver.findElements(byFourthColumn);

int size = listOfAllFourthColumnElements.size();

//get all the values of the new range of age

// also Check whether the age range is working or not.

boolean ageRangeCheckFlag = true;

for (int i=0;i<size;i++) {

WebElement individualColumn = listOfAllFourthColumnElements.get(i);

String strValue = individualColumn.getText();

System.out.println(strValue);

// get the integer value

int value = Integer.parseInt(strValue);

// check if it is in the range or not

if ((value>=iageRangeMin) && (value<=iageRangeMax)) {

// all is well continue

ageRangeCheckFlag = true;

}

else {

ageRangeCheckFlag = false;

break; // even if one number is incorrect break from the loop

}

}

System.out.println(“Age range worked fine or not = ” + ageRangeCheckFlag);

// we can add an assert statement here

 

 

CODE BASE can be downloaded from the below URL

https://www.dropbox.com/s/e3awse8pe6p25wg/DynamicDataTablePartB.java?dl=0

 

Step 4 – Click on the sort and see of the sort is working or not

 

Let us proceed on our task to further click on the column and see if the sorting works or not. The table looks like below. When we click on the Age, it sorts on the age.

Before click image is below

After click on the age column the image is as below

 

From our code perspective, we need to add the following part in our script

  1. Identify the locator for the Age column header element, click on it
  2. Store the column 4 values again into two array list, one where we will use Java sort (use Collections.sort) and one as it is. We will then compare the two lists to see if the sorting has been done properly or not.

Here is the code

DynamicDataTablePartC.java

/*

* Below code is for checking sorting once the age column is clicked

*

*/

//Click on the Age column header //*[@id=”example”]/thead/tr/th[4]

driver.findElement(By.xpath(“//*[@id=\”example\”]/thead/tr/th[4]”)).click();

//get the list of column4 webElements once again

wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(byFourthColumn));

listOfAllFourthColumnElements =

driver.findElements(byFourthColumn);

size = listOfAllFourthColumnElements.size();

 

// since we have to check if the sorting is done or not

// we will store it in two list.

// one list we will keep it as it is

// second list we will use java sorting – collections.sort

// compare the two lists

 

ArrayList<Integer> listOfValuesActual = new ArrayList<Integer>();

ArrayList<Integer> listOfValuesForSorting = new ArrayList<Integer>();

 

for (int i=0;i<size;i++) {

 

WebElement individualColumn = listOfAllFourthColumnElements.get(i);

String strValue = individualColumn.getText();

System.out.println(strValue);

int value = Integer.parseInt(strValue);

// add this value in both the lists

listOfValuesActual.add((Integer)value);

listOfValuesForSorting.add((Integer)value);

}

 

// sort one of the list

Collections.sort(listOfValuesForSorting);

 

// check if both the list are equal or not

 

boolean sortedOrNot = listOfValuesForSorting.equals(listOfValuesActual);

 

System.out.println(“Sorting is = ” + sortedOrNot);

The updated file with the new code is

https://www.dropbox.com/s/fzgy4yptjwc3w5b/DynamicDataTablePartC.java?dl=0

Step 5 – adding a Wrapper Method class

It will be a good idea to handle the common wait related issues in a common wrapper class.

  1. Many a times the elements when we try to click fails – if the element is not clickable
  2. Errors may also occur when we try to sendKeys to an element
  3. We are already handling our list by defining a wait for all elements to be visible, it will be better to create a common method which can be called whenever we want to get a list of elements.

This ensures that the code can be reused in other places and our automation code remains stable all across the enterprise use. This is how a strong enterprise wide framework utility classes and wrap around classes can evolve.

Here are the three sample wrapper method functions.

WrapperMethods

/*

* Following function is wrapper around for findElements

* it does two things

* 1. checks if all the WebElements are visible or not by a locator

* 2. use an explicit wait and then returns a list of webElements

*/

static public List <WebElement> getElementList(WebDriver driver, By byObj) {

 

List <WebElement> listObj = null;

WebDriverWait wait = new WebDriverWait(driver,waitTimeinSec);

// wait for all the elements to be visible

wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy( byObj));

 

listObj = driver.findElements(byObj);

 

return listObj;

} // end of getElementList

 

/*

* Following function is wrapper around for click

* it does two things

* 1. checks if the element is clickable or not

* 2. if it is then tries to stop the page load

* 3. then uses the standard click

*/

public static void elementClick(WebDriver driver, By byObject) {

 

// Wait for the element to be clickable

 

WebDriverWait wait = new WebDriverWait(driver,waitTimeinSec);

 

try {

wait.until(ExpectedConditions.elementToBeClickable(byObject));

// continue even if the page is not loaded

driver.findElement(By.tagName(“body”)).sendKeys(“Keys.ESCAPE”);

driver.findElement(byObject).click();

 

}

catch (Exception e) {

System.out.println(e.getMessage());

}

} // end of elementClick

 

/*

* Following function is wrapper around for sendKeys

* it does two things

* 1. checks if the element is clickable or not

* 2. if it is then tries to stop the page load

* 3. then uses the standard click

*/

public static void sendKeys(WebDriver driver,By byObject, String strText) {

 

 

try {

 

//call the click method from this class to ensure that it is clickable

WrapperMethods.elementClick(driver,byObject);

WebElement element = driver.findElement(byObject);

// clear the text box

element.clear();

element.sendKeys(strText);

}

catch (Exception e) {

System.out.println(e.getMessage());

}

 

} // end of sendKeys

 

Our code for DynamicTableHanding will now change, it will be calling the respective methods. We can refactor further – specially if we have repeated need for comparing the values or checking if the sorting is required or not. For this blog we are trying to wrap around the driver methods to make them more stable and dependable.

Here is the final code which is calling our wrapper methods

FinalCode

package cpsatblogs;

 

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

 

 

public class DynamicDataTableComplete {

 

 

public static void main(String[] args) throws InterruptedException {

// TODO Auto-generated method stub

WebDriver driver;

 

System.setProperty(“webdriver.chrome.driver”, “src/test/resources/drivers/chromedriver.exe”);

driver = new ChromeDriver();

driver.manage().window().maximize();

driver.get(“https://datatables.net/examples/plug-ins/range_filtering.html”);

 

// Date range locators

 

By byAgeRangeMin = By.xpath(“//*[@id=\”min\”]”);

By byAgeRangeMax = By.xpath(“//*[@id=\”max\”]”);

 

// as per the test data we are using 30 and 40 as the date range

String ageRangeMin = “37”;

String ageRangeMax = “40”;

int iageRangeMin = 37;

int iageRangeMax = 40;

 

// set the age range using wrapper methods

WrapperMethods.sendKeys(driver, byAgeRangeMin, ageRangeMin);

WrapperMethods.sendKeys(driver, byAgeRangeMax, ageRangeMax);

By byFourthColumn = By.xpath(“//*[@id=\”example\”]/tbody/tr/td[4]”);

List<WebElement> listOfAllFourthColumnElements =

WrapperMethods.getElementList(driver, byFourthColumn);

int size = listOfAllFourthColumnElements.size();

//get all the values of the new range of age

// also Check whether the age range is working or not.

boolean ageRangeCheckFlag = true;

for (int i=0;i<size;i++) {

WebElement individualColumn = listOfAllFourthColumnElements.get(i);

String strValue = individualColumn.getText();

System.out.println(strValue);

// get the integer value

int value = Integer.parseInt(strValue);

// check if it is in the range or not

if ((value>=iageRangeMin) && (value<=iageRangeMax)) {

// all is well continue

ageRangeCheckFlag = true;

}

else {

ageRangeCheckFlag = false;

break; // even if one number is incorrect break from the loop

}

}

 

System.out.println(“Age range worked fine or not = ” + ageRangeCheckFlag);

 

/*

* Below code is for checking sorting once the age column is clicked

*

*/

//Click on the Age column header //*[@id=”example”]/thead/tr/th[4]

 

WrapperMethods.elementClick(driver, By.xpath(“//*[@id=\”example\”]/thead/tr/th[4]”));

//get the list of column4 webElements once again

listOfAllFourthColumnElements = WrapperMethods.getElementList(driver, byFourthColumn);

 

size = listOfAllFourthColumnElements.size();

 

// since we have to check if the sorting is done or not

// we will store it in two list.

// one list we will keep it as it is

// second list we will use java sorting – collections.sort

// compare the two lists

 

ArrayList<Integer> listOfValuesActual = new ArrayList<Integer>();

ArrayList<Integer> listOfValuesForSorting = new ArrayList<Integer>();

 

for (int i=0;i<size;i++) {

 

WebElement individualColumn = listOfAllFourthColumnElements.get(i);

String strValue = individualColumn.getText();

System.out.println(strValue);

int value = Integer.parseInt(strValue);

// add this value in both the lists

listOfValuesActual.add((Integer)value);

listOfValuesForSorting.add((Integer)value);

}

// sort one of the list

Collections.sort(listOfValuesForSorting);

// check if both the list are equal or not

boolean sortedOrNot = listOfValuesForSorting.equals(listOfValuesActual);

System.out.println(“Sorting is = ” + sortedOrNot);

Thread.sleep(3000); // just for debugging purposes shud be removed

driver.quit();

 

}

 

}

The final code for both the files can be downloaded from the below URL’s

https://www.dropbox.com/s/hyjl6wjp962fptn/DynamicDataTableComplete.java?dl=0

https://www.dropbox.com/s/g81hy1pvvki8mok/WrapperMethods.java?dl=0

 

CP-SAT is the number 1 globally recognized selenium certificate. If you know selenium and you are not CP-SAT you are definitely missing something.

CP-SAT and Agile Testing Alliance is always looking for Selenium evangelists who can help in curating such blogs, help in curating conferences and our community initiatives like CPSATday and Meetups.

ATA is helping teams transform. If you have testing teams who do not know selenium automation, ATA will help you transform.

Please visit the CP-SAT Page below for more details

http://cpsat.agiletestingalliance.org/

The above blog is curated base on inputs by Adi Garg and Narendra Gupta.

If you have suggestions please get in touch with us through our linkedIn page.

https://www.linkedin.com/in/agiletestingalliance/

Leave a Reply