How to use regular expressions in Selenium WebDriver tests

 

Regular expressions are a very useful technique for improving Selenium WebDriver tests.

They can be used for

  • extracting text from the value of a webelement
  • validating if a value matches a specific pattern
  • validating if a url matches a pattern

Let’s look into regular expressions using a specific test case.

Our test case does the following:

  1. open the home page of a site (http://www.vpl.ca)
  2. execute a keyword search (example: keyword = java)
  3. the first results page is displayed
  4. validate that the url is correct (https://vpl.bibliocommons.com/search?q=java&t=keyword)
  5. validate that results count is greater than 0 (1 – 25 of 905 items; the results count is 905)
  6. select page 2 of results
  7. the second results page is displayed
  8. validate that the url is correct (https://vpl.bibliocommons.com/search?display_quantity=25&page=2&q=java&t=keyword)
  9. validate that the results count is greater than 0 (26 – 50 of 905 items; the results count is 905)

For the first validation, we should verify that the following urls are correct:

https://vpl.bibliocommons.com/search?q=java&t=keyword

https://vpl.bibliocommons.com/search?display_quantity=25&page=2&q=java&t=keyword

It would be great to use the same code to validate both urls since they are similar.

The second url just adds 2 additional parameters.

For the second validation, we should extract the results count from the following texts:

1 – 25 of 905 items

26 – 50 of 905 items

Again, we should try to use the same code to extract the result count from both texts since they have the same pattern.

For both validations, we could use String methods and variables.

But regular expressions do the same things better.

First, some details about the project

The project uses 3 classes:

  1. test class
  2. home page class
  3. results  page class

The code of the TEST CLASS is

public class Tests {
WebDriver driver;

@Before
public void setUp() {
  driver = new FirefoxDriver();
}

@After
public void tearDown() {
  driver.quit();
}

@Test
public void testResultsDetails() throws Exception {
  HomePage homePage = new HomePage(driver);
  homePage.open();

  ResultsPage resultsPage = homePage.search("java");
  assertTrue(resultsPage.resultsCount() > 0);

  resultsPage = resultsPage.selectPage(2);
  assertTrue(resultsPage.resultsCount() > 0);
}

}

The code should be very easy to understand:

  1. An object is created for the home page using the HomePage class.
  2. The homePage object uses the open() method to open the site
  3. The homePage object uses the search() method to search for a keyword

The code of the HOMEPAGE class is

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class HomePage {

WebDriver driver;
WebDriverWait wait;

By searchTextBoxLocator = By.id("globalQuery");
By searchButtonLocator = By.className("search_button");

String url = "http://www.vpl.ca/";
String title = "Vancouver Public Library - Home";

public HomePage(WebDriver driver) throws Exception {
  this.driver = driver;
  wait = new WebDriverWait(driver, 20);
}

public void open() throws Exception {
  driver.get(url);

  if (urlIs(url) == false || 
      titleIs(title) == false)
   throw new Exception("home page is not displayed");
}

private Boolean urlIs(String url) {
  return wait.until(ExpectedConditions.urlContains(url));
}

private Boolean titleIs(String title) {
  return wait.until(ExpectedConditions.titleIs(title));
}

public ResultsPage search(String keyword) throws Exception {
  WebElement searchTextBox = wait.until(ExpectedConditions.
            elementToBeClickable(searchTextBoxLocator));
  searchTextBox.sendKeys(keyword);

  WebElement searchButton = wait.until(ExpectedConditions.
            elementToBeClickable(searchButtonLocator));
  searchButton.click();

  return new ResultsPage(driver, keyword);
}

}

The HomePage class has the following methods:

  1. constructor – saves the driver object into a class field; instantiates the explicit wait object
  2. open() – opens the site and verifies that the title and url of the home page are correct
  3. search() – executes a keyword search

The last class is RESULTSPAGE.

All interesting things happen here.

Validate that the urls are correct

The code of the class is below.

All lines related to regular expressions are highlighted:

public class ResultsPage {

WebDriver driver;
WebDriverWait wait;

String keyword;

String urlRegularExpression = "https:(\\/\\/)vpl(\\.)bibliocommons(\\.)com" +
"(\\/)search(\\?)(display_quantity=(\\d+)&page=(\\d+))*(\\&)*" +
"q=([a-z]+)&t=keyword";

String title = "Search | Vancouver Public Library | BiblioCommons";

By resultsCountLocator = By.className("items_showing_count");
String pageLinksLocator = "(//a[@class='page_link '])";

public ResultsPage(WebDriver driver, String keyword) throws Exception {
  this.driver = driver;
  wait = new WebDriverWait(driver, 20);
  this.keyword = keyword;

  if (!urlContains(keyword) || 
      !correctUrlPattern()  || 
      !titleIs(title) == false)
    throw new Exception("results page is not displayed");
}

private Boolean correctUrlPattern() {
  return matchFound(urlRegularExpression, 
                    driver.getCurrentUrl());
}

private Boolean urlContains(String keyword) {
  return wait.until(ExpectedConditions.urlContains(keyword));
}

private Boolean titleIs(String title) {
  return wait.until(ExpectedConditions.titleIs(title));
}

public Boolean matchFound(String patternValue, String value) {
  Pattern pattern = Pattern.compile(patternValue);
  Matcher matcher = pattern.matcher(value);
  return matcher.find();
}

public ResultsPage selectPage(int pageNumber) throws Exception {
  WebElement page = wait.until(ExpectedConditions.
                     elementToBeClickable(pageLocator(pageNumber)));
  page.click();
  return new ResultsPage(driver, keyword);
}

public By pageLocator(int pageNumber) {
  String locator = pageLinksLocator + "[" + pageNumber + "]";
  return By.xpath(locator);
}

}

The url validations are being done by the matchFound() method which has 2 parameters:

  • regular expression to be applied to a value
  • value that will be evaluated by the regular expression

The method uses the Pattern class to create a pattern object from a regular expression.

It also uses the Matcher class to match the pattern to a value.

If the pattern matches the value, the find() method returns true.

Otherwise, the find() method returns false.

The matchFound() method is then used in the correctUrlPattern() method with 2 parameters:

  • url regular expression; the regular expression matches the urls of both pages
  • url value for the results page

We have a few problems with this code.

First, the url regular expression looks very complicated.

Second, it does not take into consideration the search keyword.

We do check if the keyword is included in the url but not through the regular expression.

Both problems are solved in the code that follows:

public class ResultsPage {

WebDriver driver;
WebDriverWait wait;

String keyword;

String title = "Search | Vancouver Public Library | BiblioCommons";

By resultsCountLocator = By.className("items_showing_count");
String pageLinksLocator = "(//a[@class='page_link '])";

public ResultsPage(WebDriver driver, String keyword) throws Exception {
  this.driver = driver;
  wait = new WebDriverWait(driver, 20);
  this.keyword = keyword;

  if (correctUrlPattern() == false || titleIs(title) == false)
    throw new Exception("results page is not displayed");
}

private Boolean correctUrlPattern() {
  return matchFound(urlRegularExpression(), 
                    driver.getCurrentUrl());
}

public String urlRegularExpression() {

  String protocol = "https:(\\/\\/)";
  String domain = "vpl(\\.)bibliocommons(\\.)com";
  String pageName = "(\\/)search(\\?)";
  String displayQuantityParameter = "(display_quantity=(\\d+)&)*";
  String pageNumberParameter = "(page=(\\d+)\\&)*";
  String keywordParameter = "q=" + keyword + "&";
  String searchTypeParameter = "t=keyword";

  String pattern = composePattern(protocol,
                                  domain,
                                  pageName,
                                  displayQuantityParameter,
                                  pageNumberParameter,
                                  keywordParameter,
                                  searchTypeParameter);

  return pattern;
}

private String composePattern(String... patterns) {
  StringBuilder completePattern = new StringBuilder();

  for (String pattern: patterns)
    completePattern.append(pattern);

  return completePattern.toString();
}

private Boolean titleIs(String title) {
return wait.until(ExpectedConditions.titleIs(title));
}

public Boolean matchFound(String patternValue, String value) {
  Pattern pattern = Pattern.compile(patternValue);
  Matcher matcher = pattern.matcher(value);
  return matcher.find();
}

public ResultsPage selectPage(int pageNumber) throws Exception {
  WebElement page = wait.until(ExpectedConditions.
             elementToBeClickable(pageLocator(pageNumber)));
  page.click();
  return new ResultsPage(driver, keyword);
}

public By pageLocator(int pageNumber) {
  String locator = pageLinksLocator + "[" + pageNumber + "]";
  return By.xpath(locator);
}

}

Instead of having the url regular expression as a very long string value, we decompose it in small parts which are then put together by the composePattern() method.

Building the regular expression this way makes the expression much easier to understand and change.

In the same urlRegularExpression() method, we use the keyword as part of the regular expression value.

By doing this, the regular expression validates not only the url template but also if the keyword value is included in it.

So we dont need an additional method for checking if the url contains the keyword.

Extract the results count

For the results count, we will need

  1. a new regular expression that matches the result count element text (1 – 25 of 905 items or 26 – 50 of 905 items)
  2. a new method that extract the result count from the result count element text (905)
public class ResultsPage {

WebDriver driver;
WebDriverWait wait;

String keyword;

String resultsCountRegularExpression = "of (\\d+) items";

String title = "Search | Vancouver Public Library | BiblioCommons";

By resultsCountLocator = By.className("items_showing_count");
String pageLinksLocator = "(//a[@class='page_link '])";

public ResultsPage(WebDriver driver, String keyword) throws Exception {
  this.driver = driver;
  wait = new WebDriverWait(driver, 20);
  this.keyword = keyword;

  if (correctUrlPattern() == false ||
      titleIs(title) == false)
   throw new Exception("results page is not displayed");
}

private Boolean correctUrlPattern() {
  return matchFound(urlRegularExpression(), 
                    driver.getCurrentUrl());
}

public String urlRegularExpression() {

  String protocol = "https:(\\/\\/)";
  String domain = "vpl(\\.)bibliocommons(\\.)com";
  String pageName = "(\\/)search(\\?)";
  String displayQuantityParameter = "(display_quantity=(\\d+)&)*";
  String pageNumberParameter = "(page=(\\d+)\\&)*";
  String keywordParameter = "q=" + keyword + "&";
  String searchTypeParameter = "t=keyword";

  String pattern = composePattern(protocol,
                                  domain,
                                  pageName,
                                  displayQuantityParameter,
                                  pageNumberParameter,
                                  keywordParameter,
                                  searchTypeParameter);

  return pattern;
}

private String composePattern(String... patterns) {

  StringBuilder completePattern = new StringBuilder();

  for (String pattern: patterns)
     completePattern.append(pattern);

  return completePattern.toString();
}

private Boolean titleIs(String title) {
  return wait.until(ExpectedConditions.titleIs(title));
}

public int resultsCount() {
  return Integer.parseInt(
           matchedGroupValue(resultsCountRegularExpression,
                             resultsCountText(),
                             1));
}

public String resultsCountText() {
  WebElement resultsCountLabel = wait.until(ExpectedConditions.
            visibilityOfElementLocated(resultsCountLocator));
  return resultsCountLabel.getText();
}

public Boolean matchFound(String patternValue, String value) {
  Pattern pattern = Pattern.compile(patternValue);
  Matcher matcher = pattern.matcher(value);
  return matcher.find();
}

public String matchedGroupValue(String patternValue, String value, int index) {
  Pattern pattern = Pattern.compile(patternValue);
  Matcher matcher = pattern.matcher(value);
  if (!matcher.find())
    throw new NoSuchElementException("couldnt find the matched group value");

  return matcher.group(index);

}

public ResultsPage selectPage(int pageNumber) throws Exception {
  WebElement page = wait.until(ExpectedConditions.
           elementToBeClickable(pageLocator(pageNumber)));
  page.click();
  return new ResultsPage(driver, keyword);
}

public By pageLocator(int pageNumber)
{
  String locator = pageLinksLocator + "[" + pageNumber + "]";
  return By.xpath(locator);
}

}

A new method, matchedGroupValue(),  is created for extracting the result count value.

It works by

  1. creating a pattern from a regular expression
  2. matching the pattern with a value
  3. if the pattern does not match the value, throw an exception
  4. if the pattern matches the value, it extracts a group from the value

The matchedGroupValue() method is then used in the resultCount() method for extracting the resultCount value from the result count element text.

Advertisement

2 Comments

  1. I’m stuck with you see eBay.ca id of username field keeps on changing everytime you run the Selenium script due to which it fails test case. How can this be solved? One way I’m thinking is using XPath on placeholder. What do you say?

    html code:

    Now this id keeps changing can you provide reg expression for it?

    Like

    Reply

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s