Sometimes, you need to interact in automation scripts with web elements that have similar HTML code.
For example, you may want to automate the following test case:
- open site
- search for keyword
- check that the results page is displayed
- select the 2nd page of results
- check that the 2nd page is displayed
- select the 3rd page of results
- check that the 3rd page is displayed
The elements for the 2nd and 3rd pages have similar HTML code (A tag, HREF and DATE-PAGE attributes):
page 2
<a href=”/catalog/category.aspx?category=29059;page=2″ data-page=”2″>2</a>
page 3
<a href=”/catalog/category.aspx?category=29059;page=3″ data-page=”3″>3</a>
There are a few options for selecting similar page elements:
- use a locator for each page element
- use a method that creates the locator for each page locator
- use a locator pattern with a parameter
The code for option 1 is below:
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.openqa.selenium.By;
import static org.openqa.selenium.By.*;
import org.openqa.selenium.WebElement;
import static org.openqa.selenium.support.ui.ExpectedConditions.*;
public class BestBuyTest extends BaseTest {
String homePageUrl = "http://www.bestbuy.ca/";
By searchTextBoxLocator = By.id("ctl00_MasterHeader_ctl00_uchead_GlobalSearchUC_TxtSearchKeyword");
By searchButtonLocator =
By.className("icon-search");
String resultsPageUrl = "http://www.bestbuy.ca/en-CA/category/";
By page2Locator = By.xpath(
"//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" + "//a[@data-page='2']");
By page3Locator = By.xpath(
"//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" +
"//a[@data-page='3']");
@Test
public void TestPaging1() throws Exception {
openSite();
searchBy("ipad");
WebElement page = wait.until(
elementToBeClickable
(page2Locator));
page.click();
assertTrue(isPageDisplayed(2));
page = wait.until(
elementToBeClickable
(page3Locator));
page.click();
assertTrue(isPageDisplayed(3));
}
private void openSite() {
driver.get(homePageUrl);
if (wait.until(
urlToBe(homePageUrl))== false )
throw new RuntimeException
("The home page is not loaded correctly!");
}
private void searchBy(String keyword) {
WebElement searchTextBox = wait.until(
elementToBeClickable
(searchTextBoxLocator));
searchTextBox.sendKeys("ipad");
WebElement searchButton = wait.until(
elementToBeClickable
(searchButtonLocator));
searchButton.click();
if (wait.until(
urlContains
(resultsPageUrl))== false )
throw new RuntimeException
("The results page is not loaded correctly!");
}
private boolean isPageDisplayed(int pageNumber) {
return wait.until(
urlContains
("page=" + pageNumber));
}
}
This option is not good because of the duplicated page locators.
Option 2 uses a method that generates the page locator:
@Test
public void TestPaging2() throws Exception {
openSite();
searchBy("ipad");
WebElement page = wait.until(
elementToBeClickable
(pageLocator(2)));
page.click();
assertTrue(isPageDisplayed(2));
page = wait.until(
elementToBeClickable
(pageLocator(3)));
page.click();
assertTrue(isPageDisplayed(3));
}
private By pageLocator(int page) {
String locatorText = "//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" +
"//a[@data-page='" +
page +
"']";
return By.xpath(locatorText);
}
The pageLocator() method creates the locator using a page number parameter.
The locator is created by concatenating 3 parts:
- the beginning of the xpath expression (before the page number)
- the page number
- the ending of the xpath expression
private By pageLocator(int page) {
String locatorText = "//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" +
"//a[@data-page='" +
page +
"']";
return By.xpath(locatorText);
}
With the pageLocator() method, there is no need of having a locator for each page element.
But the locator text was moved inside of the method.
And the method’s code is pretty complicated.
Option 3 is using a locator pattern with a parameter:
String pageLocatorTemplate = "//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" +
"//a[@data-page='%d']";
@Test
public void TestPaging3() throws Exception {
openSite();
searchBy("ipad");
WebElement page = wait.until(
elementToBeClickable
(pageLocatorFromTemplate(2)));
page.click();
assertTrue(isPageDisplayed(2));
page = wait.until(
elementToBeClickable
(pageLocatorFromTemplate(3)));
page.click();
assertTrue(isPageDisplayed(3));
}
private By pageLocatorFromTemplate(int page) {
return By.xpath(
String.format
(pageLocatorTemplate, page));
}
We use now a locator pattern with a parameter (%d):
String pageLocatorTemplate = "//div[@id='ctl00_CP_ctl00_ctl01_ProductSearchResultListing_topPaging']" +
"//a[@data-page='%d']";
A method is still needed for creating the locator.
The method has a pageNumber parameter which is substituted to the parameter (%d) of the locator pattern by the String.format() method:
private By pageLocatorFromTemplate(int page) {
return By.xpath(
String.format
(pageLocatorTemplate, page));
}
The 3rd solution is the best of the 3 presented.