-
Notifications
You must be signed in to change notification settings - Fork 2
How to map columns to positions in tables that the columns cannot be uniquely identified
Sometimes even though the row can be uniquely identified the columns cannot be uniquely identified. In these cases, you would need to use the column position in an xpath (or other locator) based on the GUI. However, this is dependent on the columns always being in the same order. The maintenance can be time consuming if a column is added and it shifts the positions of existing columns. (You may not realize this for sometime depending how your tests are written.)
A better technique is to dynamically map the columns to positions at runtime. This will ensure the proper data is allows extracted from the table and any new columns (or re-ordered columns) do not affect the results. We will look at the following table which has this problem to explain the solution:
The corresponding HTML for the table:
<table id="table1" class="tablesorter">
<thead>
<tr>
<th class="header"><span>Last Name</span></th>
<th class="header"><span>First Name</span></th>
<th class="header"><span>Email</span></th>
<th class="header"><span>Due</span></th>
<th class="header"><span>Web Site</span></th>
<th class="header"><span>Action</span></th>
</tr>
</thead>
<tbody>
<tr>
<td>Smith</td>
<td>John</td>
<td>[email protected]</td>
<td>$50.00</td>
<td>http://www.jsmith.com</td>
<td>
<a href="#edit">edit</a>
<a href="#delete">delete</a>
</td>
</tr>
<tr>
<td>Bach</td>
<td>Frank</td>
<td>[email protected]</td>
<td>$51.00</td>
<td>http://www.frank.com</td>
<td>
<a href="#edit">edit</a>
<a href="#delete">delete</a>
</td>
</tr>
<tr>
<td>Doe</td>
<td>Jason</td>
<td>[email protected]</td>
<td>$100.00</td>
<td>http://www.jdoe.com</td>
<td>
<a href="#edit">edit</a>
<a href="#delete">delete</a>
</td>
</tr>
<tr>
<td>Conway</td>
<td>Tim</td>
<td>[email protected]</td>
<td>$50.00</td>
<td>http://www.timconway.com</td>
<td>
<a href="#edit">edit</a>
<a href="#delete">delete</a>
</td>
</tr>
</tbody>
</table>
First to make the addition (or removal or modification) of columns easy to maintain, we will create an enumeration that implements ColumnMapper. This provides a default method to map the column names to the enumeration. We will use the enumeration values in the locators. (The enumeration values technically never need to change only string that stores the column name.) This the above table, the following is all that is necessary:
public enum HerokuappColumnMapping implements ColumnMapper {
LAST_NAME("Last Name"),
FIRST_NAME("First Name"),
EMAIL("Email"),
DUE("Due"),
WEB_SITE("Web Site"),
ACTION("Action");
private String columnName;
HerokuappColumnMapping(String columnName) {
this.columnName = columnName;
}
@Override
public String getColumnName() {
return columnName;
}
@Override
public ColumnMapper[] getValues() {
return values();
}
}
Next, you need construct a locator to get all the column headers (as WebElements) & code to extract the column header from the WebElement. Then, you can create a class that implements ColumnPositionsExtractor like the following:
public class HerokuappColumnPositionsExtractor implements ColumnPositionsExtractor {
@Override
public By getColumnHeadersLocator() {
return By.xpath("//*[@id='table1']/thead/tr/th");
}
@Override
public String extractHeaderAsKey(WebElement header, int position) {
return header.findElement(By.cssSelector("span")).getText();
}
}
Finally, you would override the method getSubstitutions in the table page object like the following:
@Override
protected Map<String, String> getSubstitutions() {
HerokuappColumnPositionsExtractor extractor = new HerokuappColumnPositionsExtractor();
Map<String, String> columnPositions = extractor.getMap();
return extractor.getSubstitutions(HerokuappColumnMapping.LAST_NAME, columnPositions);
}