Google Analytics

Search

To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g.

site:darrellgrainger.blogspot.com CSS selectors

will search for "CSS selectors" but only on my site.


Monday, September 23, 2013

Dealing with HTTP Basic Authentication

If you go to a website and it has HTTP Basic Authentication turned on it will pop up a dialog asking you for username and password.

You can pass this information in the URL so the dialog does not pop up. This will allow you to go to these websites with automation tools like Selenium.

For example, if you go to httpwatch and click on the Display Image button it will pop open a dialog asking you for a username and password. For this site you can enter httpwatch for the username and any string for the password. So if I go to:

http://httpwatch:password@www.httpwatch.com/httpgallery/authentication/

then click the Display Image button, it will not pop open a dialog. This is because I have already been authenticated on the website.

HOWEVER, this will not work with current web browsers.

The reason for this is because web browser manufacturers recognized the fact that people can use this URL format to create fake websites. For example, I could have a website at 10.23.56.234, turn on basic authentication and make the username www.microsoft.com. I then send out an email with the URL:

http://www.microsoft.com:80@10.23.56.234/survey.html

This URL will go to my fake website with the username:password of www.microsoft.com:80. Some people might be tricked into thinking they are going to www.microsoft.com.

Fortunately, you can turn this feature back on.

For Internet Explorer you can make it accept the above URLs. It does open you up to someone spoofing you. So do this to a test machine, used only for automation, is okay. Using this on the computer you use to surf the web is a back idea.

If you go to http://support.microsoft.com/kb/834489/EN-US it will explain all the above. At the bottom it will talk about how to enable using username:password in the URL. In a nutshell, do the following:

  • Open regedit.exe
  • Find the key HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE
  • Create the DWORD iexplore.exe=0
By setting iexplore.exe to 0 you are disabling the disabling of username:password, i.e. double negative.

If you are running 32-bit Windows this works as stated. If you are running 64-bit Internet Explorer on 64-bit Windows this works as stated. However, if you are running 32-bit Internet Explorer on 64-bit Windows this does not work. By default, 64-bit Windows 7 will run 32-bit Internet Explorer.

When you look in the registry, all the settings for 32-bit programs on a 64-bit computer will be in the WOW6432Node. So rather than HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft you want to go to HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft.

For Chrome & Firefox, they should support basic authentication through the URL.

For Safari, go into the Preferences, go to the Security tab and disable Warn when visiting a fraudulent website.

In summary, if I have the Selenium Java code:


    driver.get("http://www.httpwatch.com/httpgallery/authentication/");
    driver.findElement(By.cssSelector("#displayImage")).click();
    WebElement img = driver.findElement(By.cssSelector("#downloadImg"));

It will never make it to the third line because the .click() will bring up an HTTP basic authentication dialog which will block Selenium. Instead, I would use:

    driver.get("http://httpwatch:password@www.httpwatch.com/httpgallery/authentication/");
    driver.findElement(By.cssSelector("#displayImage")).click();
    WebElement img = driver.findElement(By.cssSelector("#downloadImg"));

.

Thursday, August 29, 2013

Little discussed Java classes in WebDriver

Sleeper

Recently I have been exposed to classes in WebDriver that I did not know existed. I started with Selenium when it was beta and found myself inventing methods to do things for Selenium and later for WebDriver. So I never really looked at the extra capabilities for WebDriver. Even before Selenium I often needed to write code that slept for a few seconds. The proper way to use the Thread.sleep method would be:

    public void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch(InterruptedException ie) {
            throw new RuntimeException(ie);
        }
    }
Today I decided to scan through the WebDriver APIs. While I was doing this I found the Sleeper class. It has two APIs which are nice to use. Rather than using the method above you can use:
    sleepTight(2000);        // sleep for 2000 milliseconds
    sleepTightInSeconds(2);  // sleep for 2 seconds
SystemClock

Another helpful class I found was the SystemClock class. It implements the Clock interface and has three functions:

    SystemClock sc = new SystemClock();

    long current = sc.now();
    long early = sc.laterBy(-60000);
    long later = sc.laterBy(60000)
    System.out.println(sc.isNowBefore(later));  // true
    System.out.println(sc.isNowBefore(early));  // false
    System.out.println(sc.isNowBefore(now));    // false
The value of 'current' will be the timestamp of now. It is similar to using System.currentTimeMillis() and the API docs actually refer to System.currentTimeMillis(). Obviously, the command immediately after sc.now() will be greater than 'current' just because the call to sc.now() will take some time. The class also has the laterBy method. It talks about passing it a positive long but you can actually pass a negative number. In this case I passed in -60000. The value is in milliseconds. So this would be -60 seconds from 'now'. The third line sets later to 60 seconds later. A few milliseconds will have passed so if the time was 1377825553203 when I called sc.now() then sc.isNowBefore() get called and set 'now' to a few milliseconds later. Maybe 'now' will be 1377825554713. So sc.isNowBefore(later) will be true because later will be around 1377825553203 + 60000 and therefore later than 'now'. Where as sc.isNowBefore(early) will be false because 'now' will be after 1377825553203 - 60000. The odd one is sc.isNowBefore(current). This is because 'current' was now when it was set but a few lines later and 'now' will be after 'current', i.e. now > current, and sc.isNowBefore(current) will be false.

ThreadGuard

This class is more for people writing multi-threaded tests. It is rare for me to write multi-threaded tests. Hopefully you will never have to go there but if you do it is important to know that the implementations of WebDriver (ChromeDriver, FirefoxDriver, InternetExplorerDriver, etc.) are not guaranteed to be thread safe. To make them thread safe you can use the ThreadGuard class. It is actually pretty simple to use. If you have:

    WebDriver driver = new FirefoxDriver();
You can make it thread safe by changing it to:
    WebDriver driver - ThreadGuard.protect(new FirefoxDriver());
It is that simple. I won't get into why you are using multi-threaded tests. If you don't know if you are using multi-threading then you are probably not using multi-threading.

UrlChecker

UrlChecker is also a helpful class. It checks to see if a URL is returning an HTTP response of 200 OK. You could use it to check for a URL existing before you navigate to it or do a if URL does not exist then go to a different URL. For example,

    try {
        UrlChecker uc = new UrlChecker();
        String testURL = "http://localhost:8080/testapp/index.html";
        uc.waitUntilAvailable(5, TimeUnit.SECONDS, new URL(testURL));
        driver.get(testURL)
    } catch(UrlChecker.TimeoutException te) {
        // if the testURL does not become available in 5 seconds
        // this code will be run
    } catch(MalformedURLException mue) {
        // needed by URL class
        // if you think the testURL might be malformed handle it here
    }

Urls

This class has two helpful static methods:

    String testURL = "http://localhost:8080/~darrell/tests/#/";
    System.out.println(Urls.toProtocolHostAndPort(testURL));
    System.out.println(Urls.urlEncode(testURL));
The first println will print "http://localhost:8080". As URLs go, the /~darrell/tests/#/ is the context root. If you try going to "http://localhost:8080" it must have a context root. It will just assume you meant "/" for the context root. The second println will print "http%3A%2F%2Flocalhost%3A8080%2F%7Edarrell%2Ftests%2F%23%2F". All the symbols are converted to their ASCII hexidecimal value prefixed with a percent symbol. So for example, %3A is a :, %2F is a /, %7E is a ~ and %23 is the #. There are other interesting classes in WebDriver but these are the less used/discussed classes. Other classes like WindowsUtils have enough happening that they really need their own article. I will say that the following are some interesting classes which require more investigation:
  • FluentWait
  • Platform
  • ProcessUtils
  • Select
  • Wait
  • WindowsUtils

Tuesday, June 25, 2013

Dealing with things that disappear from the DOM when you look at them

From time to time I encounter a javascript feature which does not let me examine it. For example, auto-complete features on input boxes.

We have all seen this feature if you go to http://www.google.ca. You start typing something and Google gives you suggestions on what you are going to type. For example, I enter "WebD" and Google suggests "WebDriver".

The problem automating some of these is finding the list of suggestions. For example, go to http://www.asp.net/ajaxlibrary/ajaxcontroltoolkitsamplesite/autocomplete/autocomplete.aspx with Chrome, Inspect the auto-complete input box (right click in the input box and select "Inspect") and type something into the input box.

You will see a list of suggestions appear as you type but looking at the DOM in the Inspect window there is no list of suggestions attached to the input element.

They have to exist somewhere in the DOM but you have to find them. So while the suggestion list is open I click on the Inspect window and press CTRL-F to find one of the suggestions.

The problem is the moment I click in the Inspect window, the list of suggestions disappears. So the CTRL-F in the Inspect window finds nothing.

If I type a word in the input box then press CTRL-F (don't change focus to the Inspect window) the auto-complete list disappears. Even if it didn't disappear, it would find the suggestions in the browser window, not in the Inspect window.

So here is how I find the suggestions in the DOM...
WebDriver driver = new ChromeDriver();
driver.get("http://www.asp.net/ajaxlibrary/ajaxcontroltoolkitsamplesite/autocomplete/autocomplete.aspx");
driver.findElements(By.cssSelector("input[name$='myTextBox']")).sendKeys("test");
String src = driver.getPageSource();
driver.quit();
then I set a breakpoint on the driver.quit(); and run in Debug mode. When it stops on the breakpoint I start using the Watch Window of my IDE.

If I enter "test" and one of the suggestions is "testASG" then I'm trying to find the list with "testASG" as one of the suggestions. So I will add:
src.indexOf("testASG");
to the Watch Window. Let's say it returns 34923. So now I know the text it at 34923 and that the element wrapping the suggestion is BEFORE 34923. So I can try:
src.substring(34500);
and examine the results. From this I might see "<li>testASG</li>" and some other stuff before the <li>. This tells me that the list of suggestion is a <ul> element (<ol> would be numbered). So I can start using:
src.indexOf("<ul");
I know it will start with <ul but there would be different, unknown attributes after the ul. When I did this it returned something like 5723. So I looked at:
src.substring(5720);
I see it is finding a "null". Not what I was looking for. So I might start looking for ul but using the index of "testASG". So I might try:
src.substring(33000).indexOf("<ul");
If that returns a number higher than 34923 (after the li) then I need a smaller number. If it gives me a number between 33000 and 34923 then I can look at the substring and see if that was the ul I was looking for.

By narrowing down (binary search) where the ul is for the list of suggestions I can finally see what the DOM looks like by examining the src variable. At some point I will figure out the locators for the ul, li, etc. I can even trying the following locator in the Watch Window:
driver.findElements(By.cssSelector("ul#AutoCompleteEx_completionListElem>li");
To get a list of all the suggestions.

Tuesday, May 7, 2013

Is an element on the visible screen

A number of times I have been on a website where clicking a link goes to a specific page with a specific element at the top of the page. For example, I am reading an article and it quotes a book. The quote is actually from page 17, paragraph 2, first sentence. So when I click the link to the article it should take me to page 17, paragraph 2, first sentence.

So how would you automate testing this?

The simple answer would be to find the WebElement which is expected to be at the top of the page. This is the easy part. Then you need to see if the WebElement is on the visible screen. If you click it, WebDriver will scroll it into view. This is not what we want.

So what do we do? The answer is find out the size of the visible window then see if the bottom-right corner of the WebElement is inside that dimension. So here is the code for that:

  private boolean isWebElementVisible(WebElement w) {
    Dimension weD = w.getSize();
    Point weP = w.getLocation();
    Dimension d = driver.manage().window().getSize();

    int x = d.getWidth();
    int y = d.getHeight();
    int x2 = weD.getWidth() + weP.getX();
    int y2 = weD.getHeight() + weP.getY();

    return x2 <= x && y2 <= y;
  }
This will tell me of the element is on the visible window. If the element is not on the visible window it will return false. If the element is on the visible window it will return true... well almost. Unfortunately, you can get the size of the browser window but you really want the size of the chrome, inside the window. So if the window dimension is 1024x768 it is really telling you how big the OUTSIDE of the window is and not the inside. If the element is located at 1024x768 it is really off the visible window. For example, on my Google Chrome the tabs/title is 22px high. So the visible screen would really be 768 - 22 or 746px.

So this is a close approximation but there is a small chance of the element being just off the bottom of the window and still return true. If you are running driver.manage().window().maximize() the odds that something is going to be off the bottom of the visible window is small enough that it is not worth worrying about. Add to that we would hopefully be manually testing the area occasionally during exploratory testing and the risk should be acceptable.

There is another problems with this. We aren't actually checking that the upper-left corner of the element is at the top of the screen. You might think, why not just check that the element we are looking for is at position (0, 0)? The reason for not checking this is because the element might not be exactly at (0, 0). If the screen is large enough for 20 elements, the page only has 4 elements and the element we are looking for is at the second position, it will be impossible for the element we are looking for to scroll. So it will be below (0, 0) but still on the visible window.

A good example if this code would be:

    driver.get("http://www.w3schools.com");
    WebElement we = driver.findElement(By.cssSelector("#gsc-i-id1"));
    assertTrue(isWebElementVisible(we));
    WebElement we2 = driver.findElement(By.cssSelector("#footer"));
    assertFalse(isWebElementVisible(we2));

This will go to www.w3schools.com, find the Google search input at the top of the page, confirm it is visible. Then it will find the footer at the bottom of the page and confirm it is not visible.

Wednesday, May 1, 2013

Why using JavascriptExecutor in WebDriver can be dangerous

I have occasionally seen people recommending running javascript using the JavascriptExecutor implementation. For example, I was working on a project where a test would pass on the developer's computer but would fail on the functional test machine. The line of code which failed was:
driver.findElement(By.cssSelector(".save")).click();
So the developer changed it to:
((JavascriptExecutor)driver).executeScript("$('.save').click();");

It seemed harmless and it worked.

The GUESS was that WebDriver found the button because it was always in the DOM with style="display: none;" but the click failed because it wasn't waiting for the button to become visible.

The idea was that the test was REALLY:

- open the dialog
- wait for the button to become visible
- click the button

If we used javascript we didn't have to wait for the button to be visible. So javascript seemed faster but was still testing the right thing.

No problem, right? Wrong. It turned out that the save button was on a javascript dialog which did not scroll. If you scrolled up and down the page the dialog remained still; the dialog ALWAYS stay exactly 120px from the top of display. When all the tests ran it created data which made the dialog 830px high. This placed the bottom of the dialog at 950px. The developers machine was 1600x1200 display. It made the dialog fully visible on the display. The functional test machine was set to 800x600. This forced the bottom of the dialog off the bottom of the display. Even when you made the browser full screen you could not see the save button.

So on the developers machine WebDriver would see and click the save button because it was visible. On the functional test machine (set to 800x600 because that was the minimum requirement) it was not visible and you could not scroll it into view because the dialog did not scroll.

When they switched to the javascript code it clicked the button regardless of whether the button was visible or not. If we shipped this application, any customer who was using a computer with 800x600 display (a significant number of people based on analytics) would be unable to click the save button even though the functional test passed.

The moral of the story is, if the javascript solution doesn't test EXACTLY the same requirement as the WebDriver solution, you could be making your tests report a false positive.

Saturday, April 13, 2013

Determining which version of Selenium goes with which browsers

Sometimes it is hard to know which version of Selenium you should be using. There is no hard and fast rule of which Selenium works with which browser but I have always found that you want a Selenium which was released after the browser but as close to the browser release date as possible.

It has been my experience that the latest version of Selenium and the latest version of Chrome go together. I have never found that the latest version of Selenium does not work with the latest version of Chrome.

On the other hand, I have seen Mozilla release a new version of Firefox and it takes a few weeks for a new version of Selenium to appear. During this period I find the latest version of Selenium does not work with the latest version of Firefox.

For example, Firefox 18.0 was released on 08-Jan-2013. At the time the latest version of Selenium was 2.28.0, released on 11-Dec-2012. If you updated to Firefox 18.0 the moment it was available, you might find that some of your Selenium tests started failing unexpectedly. Selenium 2.29.0 was released on 17-Jan-2013. I always disabled the ability for Firefox to automatically upgrade. When Selenium 2.29.0 was released, I upgraded my Selenium to 2.29.0 and my Firefox to 18.0.

You can see the release dates of Firefox (and other Mozilla browsers) on https://wiki.mozilla.org/Releases. What I do is create a list of Selenium release dates from the Selenium download area (https://code.google.com/p/selenium/downloads/list). I would then look at the Mozilla wiki and determine which Firefox had been released on an older date. From my example above:

11-Dec-2012    Selenium 2.28.0
30-Nov-2012    Firefox 17.0.1
or
17-Jan-2013    Selenium 2.29.0
08-Jan-2013    Firefox 18.0

Or in other words, if the project I am on has set a specific version of Firefox to support then I look for the Selenium which was released AFTER the Firefox release date. That is the combination I would test with.

For Internet Explorer I can find the release dates by searching for "Internet Explorer Release Dates". This currently takes me to IE Downloads. I also find the Wikipedia has an Internet Explorer page which gives release dates as well.

For the most part however, I let the version of Firefox drive which version of Selenium I need. Because I tend to have clients request IE7, IE8 and IE9 support, I find it best to go with the latest version of Selenium to support the latest version of IE.

Selenium does have limited support for Safari but this is typically not an issue. Much like IE, I let the version of Firefox drive which version of Selenium I need regardless of which version of Safari I need to support.

It should be noted that this is a general idea of how I determine which version of Selenium I want. If I use this rule and start finding that the version of Chrome matters, I would start trying to match the release date of Chrome to the release date of Selenium.


Tuesday, November 27, 2012

List of Selenium/WebDriver blogs.

I was recently pointed to a nice list of Selenium/WebDriver blogs. If you are looking for more information about Selenium/WebDriver you should check out http://it-kosmopolit.de/Selenium/blog/selenium-blogs/selenium_blogs.php.

I haven't checked out all the links but there appears to be a good start to the list of Selenium/WebDriver blogs available out there.