How to handle nested tables with Webdriver

1,637 views
Skip to first unread message

Gagan

unread,
Oct 30, 2012, 6:38:57 PM10/30/12
to webd...@googlegroups.com
Hi All,

I'm struggling with this old application which has nested tables within tables and table within a cell. For example when I search using the following xPath, I'm expecting 1 row to be found because I specified tr[1].
//div[@class='wrapper']/table[@class='simple']/tbody/tr[1]/td[5]

This however, finds 3 different rows because my table has 3 tbody tags. Is there a generic way to to do this when nested tables are there? All I want to do is click the link on row one.

 I tried this  xPath: //div[@class='wrapper']/table[@class='simple']/tbody/tr[1]/td[6]/table/tbody[1]/tr/td[1]/a[1]

This returned me 9 elements on the page. I can pinpoint the row 1 link by specifying tbody[1] in the xpath but my goal is to go throw all the rows in all tbody's and compare with my expected results. If there is a match then click the link in that row.

WebElement table_element = driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']"));
        List<WebElement> tr_collection=table_element.findElements(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody/tr"));
        System.out.println("NUMBER OF ROWS IN THIS TABLE = "+tr_collection.size());
       
        for(int i=1; i<=tr_collection.size();i++){
            String procedure = driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody/tr["+i+"]/td[5]")).getText();
            System.out.println("procedure found on row: "+i+ " is : "+procedure);
            driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody/tr["+i+"]/td[6]"));
        }

Please tips are appreciated.

Thanks,



Ross Patterson

unread,
Oct 31, 2012, 8:38:32 AM10/31/12
to webd...@googlegroups.com

“tr[1]” doesn’t mean “one row”, it means “the row that is the first child of its parent”.  Your “//div[@class='wrapper']/table[@class='simple']/tbody/tr[1]/td[5]” XPath means “Any TD that is the fifth child of any TR that is the first child of any TBODY that is a child of any TABLE with the class ‘simple’ that is a child of any DIV with the class ‘wrapper’ anywhere in the page”.  When you read it that way, it should become clear how it could match several TD tags.  At the worst case, you could use “//dev[@class=’wrapper’]/table[@class=’simple’]/tbody[1]/tr[1]/td[5]” to match only the first , but when you depend on counting elements, you often wind up with brittle locators, that break easily with application changes.  Except for the ugliest of HTML usage, there is usually a way that doesn’t depend on counting.  For example, if the table you want contains a title row or some other marker text, you can include it: “//div[@class='wrapper']/table[@class='simple' and tbody/tr[1]/td[1]/text() = ‘Table title’]/tbody/tr[1]/td[5]”.

 

Ross

Message has been deleted

Gagan

unread,
Oct 31, 2012, 2:07:33 PM10/31/12
to webd...@googlegroups.com

Ross. Thank you for your response. I found the solution, which was to get the count of tbody tags at run time and then get the count of rows within that tbody. This way I was able to loop through all the rows and fetch the data from the cells. Here is the example, I hope it help others:

        //Find the results table

        WebElement table_element = driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']"));

        //Get the count of the tbody's found within the results table
        List<WebElement> tbody = driver.findElements(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody"));

        //Get the total of rows in all tbody elements
        int myTbody = tbody.size(); //# of tbody elements found within the table
        List<WebElement> tr_collection=table_element.findElements(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody[*]/tr[*]"));

        System.out.println("NUMBER OF ROWS IN THIS TABLE = "+tr_collection.size());
      
     
      
        String foundProcedureName=null; //Stores procedure name found at run-time for comparison
        boolean foundFlag =false; //returns true when expected procedure is found
      
        //Loop through tbody elements
        for (int j=1;j<=myTbody;j++){
          
          
            //Loop through all rows found within the tbody[j] element

            for(int i=1; i<=tr_collection.size();i++){
                tr_collection=table_element.findElements(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody["+j+"]/tr"));
              
                //Get the procedure name
                foundProcedureName = driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody["+j+"]/tr["+i+"]/td[5]")).getText();
                //System.out.println("procedure found on row: "+i+ " is : "+procedure);
                  
                    //Compare if found procedure name contains the expected Procedure name
                    if (foundProcedureName.contains(expectedProcedureName)){
                        System.out.println("procedure found on tbody: "+j+ " and row number: "+i);
                        String foundProcedureDate = driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody["+j+"]/tr["+i+"]/td[2]")).getText();
                        System.out.println(foundProcedureDate);
                          
                            /*If procedure name is found then compare is procedure date matches the expected date
                              If it does then click on the link*/
                            if(foundProcedureDate.contains(expectedProcedureDate)){      
                                Thread.sleep(1000);
                                driver.findElement(By.xpath("//div[@class='wrapper']/table[@class='simple']/tbody["+j+"]/tr["+i+"]/td[6]/table/tbody/tr/td/a")).click();
                                Reporter.log("Found procedure: "+expectedProcedureName+ " for patient: "+patientID);
                                foundFlag = true;
                                break;
                        }
                        if (foundFlag)
                            break;
                      
                }
                    if (foundFlag)
                        break;
        }
            if (foundFlag)
                break;  
}
        if(!foundFlag){
        Assert.assertTrue(foundProcedureName.contains(expectedProcedureName), "Expected procedure: "+expectedProcedureName+ " " +
                "with exam date of: "+expectedProcedureDate+ " not found on access patient results screen for patient: "+patientID);
        }

    }
Reply all
Reply to author
Forward
0 new messages