How to deal with windows authentication popups

auth-required-basic

 

If you dont like Bugs Bunny, maybe you should not continue reading.

 

What is the first thing that I do every morning at work?

Check the results of the automation scripts executed in Jenkins over night.

I want to know how many scripts passed or failed because of the work done in the previous day.

Most days, some scripts pass and some fail.

But recently, most scripts failed.

This was very unusual so I looked into it right away.

All failures followed the same pattern: the scripts failed on opening the site in the browser.

 

Bugs-Bunny-Looking-Shocked

 

 

A sample script looks like this:

@Test
public void canBrowseThroughPages() {

  HomePage homePage = new HomePage(driver);
  homePage.open();

  ResultsPage resultsPage = homePage.search(keyword);

  assertTrue(resultsPage.resultCount() > 0);
 
  resultsPage = resultsPage.selectNextPage();

  assertTrue(resultsPage.isCurrentPage(2));
  assertTrue(resultsPage.resultCount() > 0);

}

 

The error happened in all scripts on the second line:
homePage.open();

The error info was

java.lang.object.RuntimeException:
home page didnt load correctly:
expected url = http://www.testsite.com
actual url =

 

I looked at the code of the open() method:

public class HomePage {

  private WebDriver driver;
  private WebDriverWait wait;

  private String url = "http://www.testsite.com";

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

  public void open()

    try {
      this.driver.get(url);
    }
    catch (Exception ex) {
     throw new RuntimeException("home page cannot be opened!");
    }

    try {
     wait.until(urlContains(url));
    }
    catch (Exception ex) {
     throw new RuntimeException("home page didnt load correctly - " +
             "expected url = " + url +
             " - " +
             "actual url = " + this.driver.getCurrentUrl());
    }
  }

 

I could not find anything wrong in the method.

It tries to open the url.
If it cannot, it throws an exception.

If the url is opened, it tries to verify the page url.
If it cannot, it throws an exception.

 
So, I looked again at the exception info:

java.lang.object.RuntimeException:
home page didnt load correctly;
expected url = http://www.testsite.com
actual url =

 

The actual url is empty which means that the site was not loaded.

But, why is the second exception thrown instead of the first one?

If driver.get(url) does not work, why doesn’t it generate an exception?

 

bugs bunny puzzled

Strange, isn’t it?

 

Next, I execute the script locally in Eclipse and it works.

So, the script works locally but has an error when executed in Jenkins.

 

What is the issue?

I look at the Jenkins execution details and get the name of the Jenkins slave where the script ran.

I start a remote connection to that server, open Chrome and load the site url in it.

And surprise!

A windows authentication popup is displayed!!!

That’s why the second exception was thrown instead of the first.

When executing driver.get(url), before the url is launched in the browser, the windows authentication popup is displayed.

driver.get() passes and the code continues with the url validation.

Since the authentication popup is still displayed, the page is not loaded and the url validation fails.

So the authentication popup is the culprit.

 

How can I disable the windows authentication popup?

Resetting the chrome browser settings does not help.

I can try creating the chrome driver with options and adding the site url to the chrome authentication whitelist.

This works manually.

When added to the code, the authentication popup is still there:

ChromeOptions options = new ChromeOptions();
options.addArguments("auth-server-whitelist='www.testsite.com'");
ChromeDriver driver = new ChromeDriver(options);

 
What else can I do?

Nothing goes right ……………….

So …………..

 

nothing goes right

 

Lets go left 🙂

 

I can include the account’s username and password in the url:

public class HomePage {

  private WebDriver driver;
  private WebDriverWait wait;

  private String url = "http://www.testsite.com";

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

  public void open() {

    try {
      this.driver.get(urlWithAccount(url));
    }
    catch (Exception ex) {
      throw new RuntimeException("home page cannot be opened");
    }

    try {
      wait.until(ExpectedConditions.urlContains(url);
    }
    catch (Exception ex) {
      throw new RuntimeException("home page didnt load correctly - " +
                                 "expected url = " + url +
                                 " - " +
                                 "actual url = " +                                                             this.driver.getCurrentUrl());
    }

  }

  private urlWithAccount(String url) {
     String username = System.getProperty("username");
     String password = System.getProperty("password");

     String newUrl = url.replace("http://",
                                 "http://" +
                                 username +
                                 ":" +
                                 password +
                                 "@");

     return newUrl;
  }

 

With the new method, the url would changes from

http://www.testsite.com

to

http://username:password@www.testsite.com

 

Rerunning the Jenkins scripts again showed that the problem is resolved.

 

thats all folks

Advertisements

How to use locators with parameters

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:

  1. open site
  2. search for keyword
  3. check that the results page is displayed
  4. select the 2nd page of results
  5. check that the 2nd page is displayed
  6. select the 3rd page of results
  7. 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:

  1. use a locator for each page element
  2. use a method that creates the locator for each page locator
  3. 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.