Selenium WebDriver – How To Locate WebElements from Dynamic Tables

Overview:

In this article, Lets see how we could find elements from a dynamic web table when there are no proper locators.

Problem Statement:

I have a web application in which I have around 1800 HTML elements to deal with! It is a very heavy page. The challenge is here not only the number of elements – but also the locators. Most of elements do not have any proper locators. I had to find the elements using the associated label. But the labels itself are dynamic!!! In my BDD style framework, I will get the associated label of the element to interact with. It could be anything which I can not hard code in my test. The approach had to be generic.

Hopefully you get the challenge I had!!

I was asked to find a better solution here – I was looking for some robust approach, then I ended up having – Find-By-Map – approach which works just great!

As I might not be able to share my actual application details, Lets use some common application to understand my approach better.

Demo Application:

Lets consider this sample application – http://phptravels.net/admin – This is a sample application to practice selenium.

Credentials:

username=admin@phptravels.com
password=demoadmin

Lets go to this page – http://phptravels.net/admin/locations

demo-application-001

Complexity wise, it is not very close to my actual application. But I can explain the approach using the above table. Each city in this table has a check box, edit button and delete button. It also has other information – Lets ignore those. Cities are dynamic. City name could be anything. Lets assume you have a requirement to click on a check box / edit button / delete button for a given city.

For ex:

  • Click on Edit button for Atlanta
  • Click on the Delete button for Delhi etc.

For each city, I had to collect the check box, Edit button and Delete button.

demo-application-003

Creating a JavaScript Map:

I wanted to create a map as shown here.

{
   "Kauai":{
      "CheckBox":   [HTMLElement],
      "Edit"    :   [HTMLElement],
      "Delete"  :   [HTMLElement]
   },
   "Kapaa":{
      "CheckBox":   [HTMLElement],
      "Edit"    :   [HTMLElement],
      "Delete"  :   [HTMLElement]
   },
   "Hoonani":{
      "CheckBox":   [HTMLElement],
      "Edit"    :   [HTMLElement],
      "Delete"  :   [HTMLElement]
   },
   "Ajman":{
      "CheckBox":   [HTMLElement],
      "Edit"    :   [HTMLElement],
      "Delete"  :   [HTMLElement]
   },
   ...
   ...
   ...
}

So that I could use the map to get the element. That is,

map.get("Atlanta").get("CheckBox").click()

In order to create the above map, I inject below javascript code.

var locations = {};
Array.from(document.querySelectorAll('.xcrud-row'))
    .map(function(ele) {
        return locations[ele.querySelectorAll('td')[2].innerText.trim()] = {
            'CheckBox': ele.querySelector('ins'),
            'Edit': ele.querySelector('a[title="Edit"]'),
            'Delete': ele.querySelector('a[title="DELETE"]')
        };

    });
return locations;

I verified my javascript in the chrome console.

demo-application-005

 

Once JavaScript is verified in your chrome console, we can use JavascriptExecutor to convert the HTMLElements to WebElements.

public Map < String, Map < String, WebElement >> getLocationsMap() {
     String queryString = "var locations = {};Array.from(document.querySelectorAll('.xcrud-row')).map(function(ele) {return locations[ele.querySelectorAll('td')[2].innerText.trim()] = {'CheckBox': ele.querySelector('ins'),'Edit': ele.querySelector('a[title=\"Edit\"]'),'Delete': ele.querySelector('a[title=\"DELETE\"]')};}); return locations;";
     Object obj = ((JavascriptExecutor) driver).executeScript(queryString);
     Map < String, Map < String, WebElement >> map = (Map < String, Map < String, WebElement >> ) obj;
     return map;
}

Find Elements Using Map:

That is it!!! You have found all the elements, we are interested in, on the page and created a map. Now Lets test this. (I ignored the navigation part to test this page)

 //Get the location map - for each location - we need a map for checkbox, edit and delete buttons
  Map < String, Map < String, WebElement >> map = adminLocations.getLocationsMap();

  //Click on checkbox for below locations
  Arrays.asList("Kauai", "Kapaa", "Muscat", "Singapore")
        .stream()
        .forEach(loc -> map.get(loc).get("Checkbox").click());

  //delete karchai - shows an alert for confirmation - accept it
  map.get("Karachi").get("Delete").click();
  driver.switchTo().alert().accept();

I could get the city name and element to be clicked as parameters to do operations on this table. It just works great!!

Demo:

Summary:

I prefer JavaScript approach. Because, I was looking for a very generic approach. So that I could have my own annotation to inject and convert the HTMLElements to WebElements. The complexity to find elements is hidden in the js file.

@FindByJS("js-injection-file.js")
private Map map;

JavaScript approach is littler bit faster. But nobody cares of performance in Functional Test automation which makes sense. Another advantage is you could inject JQuery like libs to make use of the lib features in your JavaScript file.

 

Happy Testing & Subscribe 🙂

 

 

Share This:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.