How to use the Firefox Marionette Driver

Note: This post was triggered by a blog post on David Burns’ Blog which states that FirefoxDriver will stop working at version 47 of Firefox. 

It is easy to switch to using the new MarionetteDriver.

  • I suggest you try it on your code, and if your tests work, then keep using it.
  • If your tests fail, then it is most likely some defects in the newer MarionetteDriver. In which case:

Steps to switch:

  • Download the marionette driver
  • Unarchive it to a folder of your choice
  • In your code set the "webdriver.gecko.driver" property to the location of the .exe
  • Use the MarionetteDriver

The above is slightly different from the official instructions on the page.

The official instructions suggest you:

  • rename the file to ‘wires.exe’ and
  • add it to your path.

I did rename it to wires.exe, mainly to keep the code consistent so I wouldn’t have to update the code everytime I change the version of the marionette driver.

But I didn’t add it to the path. I’d rather control that in the code when the option is provided.

So for Firefox 45 and 46 my start driver code looks like this:

WebDriver driver = new FirefoxDriver();

And for Firefox 47 and above my code looks like this:

String currentDir = System.getProperty("user.dir");
String marionetteDriverLocation = currentDir + "/tools/marionette/wires.exe";
System.setProperty("webdriver.gecko.driver", marionetteDriverLocation);
WebDriver driver = new MarionetteDriver();

I have a working example of this in the startUsingSeleniumWebDriver code on github:

Posted in FAQ, Selenium Simplified Blog, WebDriver | 26 Comments

Coding for workarounds so you know when they are fixed

With most libraries you use to automate your work, you have to code workarounds.

I coded a workaround in my Selenium WebDriver tests because there was a bug in ChromeDriver which meant that cookies were created differently than other browsers.

When I upgraded to WebDriver 2.53.0 and ChromeDriver 2.21 I discovered I didn’t need my workaround anymore.

In the code I had one set of code for Chrome, and another set for other browsers:

// chromedriver has a bug where the domain is prefixed with '.' 
// the app 'updates' a new cookie so when chromedriver
// returns the cookie it returns
// the first and not the updated one
if(Driver.currentBrowser()== Driver.BrowserName.GOOGLECHROME){
  aCookie = getCookieWithLargestValueNamed
}else {
  aCookie = driver.manage().getCookieNamed(

The bug was 7499 and was fixed in the recent update to ChromeDriver.

How do I know it was fixed?

Because I had failing @Test methods.

Tests in error:
  Chrome cookie creation issue has been resolved
  Chrome cookie creation issue has been resolved

Why did they fail (the workaround was still valid)?

Because I had code that checked for that.

// This currently fails on Chrome Driver since
// ChromeDriver adds some extra cookies when it creates 
// a cookie with a domain
if(Driver.currentBrowser() != Driver.BrowserName.GOOGLECHROME){
  assertEquals("we added the cookie correctly", cookieCount, 
    throw new RuntimeException(
                 "Chrome cookie creation issue has been resolved");
// also Chromedriver now adds a '.' in front of the domain

I added extra code to throw an error, if the problem I was coding a workaround for went away.

I don’t know if that is a normal practice. But since I don’t really want workarounds in my code forever, and I likely won’t remember to check the release notes on every fix to see if I coded a workaround for a fix. This seems like a good workaround to check if workarounds are no longer good.

The code I was using to check for the largest value in a cookie, by the way, was:

private Cookie getCookieWithLargestValueNamed(String cookieName) {
    int largestCookieVal=0;
    Cookie largestCookie=null;
    for(Cookie aCookie : driver.manage().getCookies()){
            int cookieVal = Integer.parseInt(aCookie.getValue());
            if(cookieVal>largestCookieVal) {
                largestCookieVal = cookieVal;
                largestCookie = aCookie;
    return largestCookie;


Posted in Practices, Selenium Simplified Blog, WebDriver | 5 Comments

Upgrade to Selenium WebDriver 2.53.0 and ChromeDriver 2.21

Selenium WebDriver 2.53.0 is available
Selenium WebDriver 2.53.0 is available

Upgrade to Selenium WebDriver 2.53.0

I’ve just updated my online Selenium 2 WebDriver with Java course to use version 2.53.0 of Selenium WebDriver. Change Log

I mainly update due to browser changes but this update required a code fix.

Selenium WebDriver 2.53.0

Minor code fix in the pom.xml to add a dependency on the new Htmlunit driver project.

The package structure in the htmlunit driver was left the same so all I had to amend was the pom file


And the include for HTMLUnit became:

<!-- from WebDriver 2.53.0 HTMLUnit is no longer distributed with Selenium WebDriver -->
<!-- -->
<!-- need to bring in the htmlunit driver on its own -->
<!-- -->

ChromeDriver 2.21

I also had to update my build machine to use 2.21 ChromeDriver.

When I did this I discovered that this version has fixed some Cookie bugs when working against Google Chrome.

So I amended my tests to cater for this as well.

I’ve uploaded the code to my online training course existing students can download the amended code from the course materials section.

Posted in Selenium Simplified Blog, Training Courses, WebDriver | Leave a comment

Can I use Selenium WebDriver to automate a Windows Desktop Application?

A question posted on the forum of my Selenium WebDriver course. “Can I use Selenium WebDriver to automate a Windows Desktop Application?”

I have updated and expanded the answer I provided on the course forum into this blog post.

Answer:  No. WebDriver is Web only. But…

Since WebDriver has a RemoteWebDriver architecture, people have created RemoteWebDriver implementations which aim to automate Windows Applications:

Some options that I have not tried, based around WebDriver:


I have not had to automate windows for a long time.

When I last automated Windows I used AutoIt:

I started work on my own Java Windows Automation API, but I haven’t kept that up to date since I haven’t had to automate windows. JWinAuto on sourceforge.

Andrew Davis has created a similar implementation called JAU.​

Also available is the AutoIt4Java implementation:

You might also consider AutoHotKey, a simple language like AutoIt and widely used.

I have heard of…

I have heard of people successfully using White:

I have also heard people successfully use Sikuli:

Other standalone tools:

Posted in Selenium Simplified Blog, WebDriver | 10 Comments

Test Automation in Cybernetics And Management

I recently re-read Stafford Beer’s 1967 “Cybernetics and Management”. An overview of Cybernetics and its derivation from Operational Research; because its examples use organisations and teams, it provides immediate relevance to my work.

I re-read this because I like to understand ‘automation’ from a more historical perspective, rather than focus on it from the perspective of ‘test automation’.

An Unfortunate Word

A paragraph towards the end of the book stood out:

“… Automation, in short, has turned out to be an unfortunate word, because it seems to say that there is something there to be made automatic. That is to say “automation” seems to imply that we know what to automate, which is by no means true.

I have come to view ‘Automation’ as “an unfortunate word” and in some of my Keynotes fron 2015, I recommended avoiding the use of the ‘automation’ word altogether.

As a word, ‘Automation’ can be interpreted as:

  • a process of automating something, and
  • a thing in its own right

… possibly other interpretations that I can’t think of at the moment.

Automation as a process

The process of automating something doesn’t present as many problems when communicating if I avoid the ‘automation’ word.

I could say:

“I am writing code to execute ‘path X’ through the system using the data set Y and check conditions in the set Z”

And that fairly clearly summarises my current task (if we know what ‘X’,’Y’ and ‘Z’ refers to).

If I replace ‘automating’ with ‘automation’, then I communicate with more ambiguity:

  • “I’m doing test automation” (automation as a verb)
  • “I’m creating test automation for path X”, (automation as a noun)
  • “I’m writing test automation” (automation as a noun)

I could say:

“I am doing test automation by writing code to execute ‘path X’ through the system using the data set Y and check conditions in the set Z”

And if I do, I probably inserted ‘test automation’ as a marketing phrase because I think the listener will zone out when I start talking about the actual details of my task.

Against Automation as a Thing

We can count Things. We can cost Things.

This leads to ‘test automation’ as a metric-able, ‘return on investment’-isable, management obfuscation-isable concept.

Which has a tendency to encourage work and discussions mired with red tape.

If we avoid the word ‘automation’ then we can avoid much of this.

What other words can we use?

Norbert Weiner wrote a paper called “Some Moral and Technical Consequences of Automation” where he uses the word ‘Automation’ in the title.

A good copywriting technique involves using words that your readership understands, so that they then read the next sentence in the article, and keep doing that to build an effortless flow through the article. A technique I have yet to consciously exploit.

In the body of the article itself Weiner uses words such as: program, automata, automate, automatization, strategy, machine, automatic, programming, etc.

Words which are very specific in their meaning, particularly when read in context.

Weiner avoided writing ‘Automation’.

We know what to automate

And back to Stafford Beer,:

‘automation’ seems to imply that we know what to automate, which is by no means true

The conjoint concept ‘test automation’ has the issue that because it relates to ‘test’ we might look at the visible portions of ‘test’ and then ‘automate’ what we see or have the ‘automation’ done by the people who ‘test’.

Rather than looking at the system of ‘testing’ and automating to improve or change the system.

We can automate to expand our possibilities rather than simply automate existing parts of the sub-system and then work around those.

Earlier in the same book

On pages 214 to 217 Stafford Beer describes many of the problems that I see written of today as ‘Test Automation Issues’

“The machines worked very fast, and looked like replacing large numbers of clerical staff – consequently the costing exercises indicated a favourable outcome for the machine. In the event, the staff savings were normally over-estimated; and the typical picture become one of disappointed managers finding that, after very intense effort and many teething troubles, they had a quired a new procedure which was not much better than the old – and certainly not much cheaper.”

Hmm, I think I’ve seen this written in terms of a ‘Test Automation’ issue.

“.. is is common nowadays that large companies boast about the *number* of computers they have… one looks in vain for accounts of the astounding new modes of control which have been made possible…”

Because ‘things’ are countable. And I view having more ‘things’ as better than having fewer ‘things’.

“The fact is that we should not have been trapped by our cost management technique into comparing existing procedures with their possible automated replacements. Because the exiting procedures set out to solve problems which arose in a computerless world, and those problems were generated by an organisational structure designed for a computerless world.”

A Knockout Punch

On page 217 Stafford Beer hits us with an uppercut to floor us:

“How can I use the computer in my enterprise?” … the question was wrong. A better question would have been “What should my enterprise be like, now that computers exist?”

It can take experience to answer this because you may not know the possibilities that have been opened up.

But if you have conducted experiments with various tools and approaches then you can start to expand your view of how automating might help you adapt your test process the the future variety in the System of Development.

And an easy first step? Stop using the word ‘automation’.


Like most Stafford Beer books “Cybernetics And Management” tends to sell at inflated second hand prices, but you can usually find affordable copies if you know where to look


Posted in Practices, Selenium Simplified Blog | 4 Comments

TestWorksConf 2015 Notes and Information

I have added the slides for my TestWorksConf keynote to my TestWorksConf page. So if you’d like to see them then you can head off here. This post is to describe the rest of the conference and link to useful sources of information I think you’ll find interesting.

The conference (#testworksconf) was probably the most ‘hands on’ conference I have ever attended. Xebia, the organisers, created a usb stick with a VM which had all the software that would be used in the workshops and demonstrated by the speakers so that the conference attendees could hack along with the talks.

Because of the information provided at the conference, I have had my first exploration of Gatling. I was also able to spend some time with Erik Swets and he helped me use Gatling against (which I was unaware of until Erik showed me).

Machiel van der Bijl pointed me towards a lot of Model Based Testing resources that I need to find time to investigate in more detail. During the conference Machiel provided a quick overview of Model Based Testing and demonstrated the tool that Axini have been working on.

Ivan Shubin provided a really good interactive demo of his Galen Framework visual testing tool. Galen has now bumped its way a little higher up my investigation todo list and I think is worth checking out.

Maarten van den Ende and Adé Mochtar provided an overview of Selenium Grid in containers using the 2.47.1 Docker images and expanding it further with Mesos. (slides)

John Smart provided the closing keynote with a demo of his Serenity tool, and more interesting the ‘Journey Pattern’ Actor approaches he uses to cut down on the amount of code in page objects and other abstraction layers. It was different enough from the type of approaches that I covered in my Abstractions talk that I need to look through John’s example code in more detail. Fortunately, John also used the TodoMVC app as his example target.

You’ll be able to see Erik and Maarten talking at Agile Testing Days this year, covering the topics above.

Also at the conference; an overview of updates to FitNesse, an intro to the Robot Framework and Cucumber.JS/Angular and Protractor. And a very interesting introduction to the Mox Angular mocking library.

Unfortunately there was too much on for me to go to every talk. A very interesting conference, very hands on, with lots to learn.

Hopefully I’ll be able to attend on future years as well.

Posted in Conferences, Selenium Simplified Blog | 5 Comments

Lessons learned from a cloud grid bug

Lessons Learned from a Cloud Grid Bug

Because my Selenium WebDriver with Java course covers as much of the Selenium WebDriver API as I can, I often have code usage in there that doesn’t see the light of day on many live projects. Therefore it can act as ‘edge cases’ for drivers and grid installations.

A few days ago the course code identified a problem on TestingBot, which they have now fixed.

All my @Test code now runs clean on the TestingBot environment.

This post describes the issue that TestingBot fixed, and some generic lessons learned that I draw from this situation.

Finding The Bug

I ran my @Test code from a Jenkins CI, configuring the grid setup using properties passed in as -D values.

  • firefox
  • version: 37
  • platform: WIN7

I isolated the problem to a specific action in my code by running the first listed failing JUnit tests in the Jenkins build.

I did this, but running the specific methods from IntelliJ and configuring my execution through Environment Variables via the “run \ edit configurations” functionality.

I found the test that triggered the hang:

public void submitFormWithDropDownFiveUsingKeyboardSpecialKeys(){

     driver = Driver.get("" +

    WebElement dropDownSelect;
    dropDownSelect = driver.findElement(By.cssSelector("select[name='dropdown']"));



    new WebDriverWait(driver,10).until(ExpectedConditions.titleIs("Processed Form Details"));


I could see on the TestingBot “Test Steps” output that it had executed the findElement statement, but wasn’t executing the sendKeys, and it seemed to be freezing when executing the sendKeys.



I emailed this issue off to TestingBot via the support contact form and received an email two days later letting me know the issue had been fixed.

… turns out it was a character encoding issue we had in our grid. The Keys.chord sends out Unicode characters, which somewhere in our grid was wrongly converted, causing the selenium node to hang. We use nginx in front of our grid, which in turn was stalling on the request, only timing out after 900 seconds.

So, all good.

Lessons Learned

And some generic lessons learned:

  • Our automated code often has workarounds in it due to environment
  • Maintaining a grid can be hard
  • Cloud Grid systems, by default, have lots of logging support
  • Your code config should support CI and local debugging
  • Investigate and raise bugs

Our automated code often has workarounds in it due to environment

If I was working on site where this code failed on my grid, I’d probably just change the code and find another way of selecting the items in the drop down. And to be fair to TestingBot, this isn’t the way that most people could select an item in a list, but my Selenium WebDriver with Java course is designed to teach the normal ways, and demonstrate alternative approaches because, when I automate, I often have to find workarounds to:

  • browser issues
  • Webdriver versioning issues
  • environment issues
  • system bugs
  • etc.

Maintaining a grid can be hard

To be honest. If this had happened on a local grid because of network configuration issues in our environment. I don’t think I’d have been able to track it down and fix it.

I’d have been able to create a workaround in my code, but what would happen next time there is an issue?

Maintaining a local grid can be hard, and takes time to keep it up to date, and up and running. That’s why I try to keep my grid configurations pretty simple, and use cloud based grid solutions when I need a grid with lots of versions and combinations.

Cloud Grid systems have lots of logging support

One thing I encounter on live sites is that people often write logging code very early into their testing ‘frameworks’ because they don’t have the kind of default logging support that you saw above on the output from TestingBot GUI.

The cloud grid systems (Saucelabs, BrowserStack, TestingBot) all show the WebDriver protocol messages received, and capture movies or screenshots of the code executing against the browser.

Consider in your own work environment whether you should write the logging code yourself, or take advantage of the logging already built into the cloud grid systems.

Your code config should support CI and local debugging

I described in a blog post some ways of configuring your driver abstractions.

This situation exemplifies why it is needed. I was able to run the @Test code via CI, and very quickly, run the individual @Test method in the IDE in debug mode. If you have to fiddle with config files or find this hard to do, then I recommend you revisit your code so you have the flexibility you need to run your test code as and when you need to.

Investigate and Raise Bugs

Do raise bugs when you find them. You don’t always get a good response like I did from TestingBot. i.e. they thanked me, and actually fixed the problem

But with a single fix, TestingBot became a viable platform for me to consider as competition to SauceLabs and BrowserStack. That’s a good result.

Posted in Grid, Selenium Simplified Blog | 2 Comments

Generic Selenium WebDriver Grid Configuration Handling to use BrowserStack, Saucelabs and TestingBot

Image taken from page 103 of ‘Der Beobachter. Allgemeine Anleitung zu Beobachtungen über Land und Leute.

In my Selenium WebDriver with Java course I have a abstraction class.

I use an abstraction class so that in the @Test methods we make a call to Driver.get rather than individual firefox or chrome drivers, or RemoteDrivers

And the configuration of this is adjusted via the actual code, or a mix of environment variables and properties.

I was looking at some Mac Grid issues that I was helping Brian Long investigate. And I realised that my Generic Grid handling wasn’t as Generic as it needed to be to support my debugging.

I amended my code so that I could configure my with either environment variables or properties:

  • Environment Variables are handy because they are system wide and you can configure them on the machine, but they are a bit of a pain to work with because you have to restart the console and IDE when you change them.
  • Properties are handy because you can pass them into mvn test jobs with -D parameters e.g. mvn test -Dparam=value and this makes it very easy to use them in CI

I primarily use IntelliJ and in the Run \ Edit Configuration section you can override environment variables, and pass in -D params so it is possible to work with these in the IDE.

I find it easier to override environment variables in the IDE because the GUI makes it easy to amend them in a list.

I find it easier to configure the CI jobs through properties by passing them in as -D to mvn test

Therefore I amended my code to not care if its configuration comes from environment variables or properties


String gridBrowser = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER", "firefox");
String gridBrowserVersion = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER_VERSION", "");
String gridBrowserPlatform = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER_PLATFORM", "");

DesiredCapabilities gridCapabilities = new DesiredCapabilities();

And getPropertyOrEnv looks as follows

 * Allow setting the controls via property or environment variable
 * property takes precedence, then environment variable, then default
private static String getPropertyOrEnv(String name, String theDefault){

    String theValue = System.getProperty(name);
    if(theValue == null){

        System.out.println("Could not find Property " + name);
        theValue = System.getenv(name);


            System.out.println("Could not find Environment Variable " + name + " using default value " + theDefault);
            theValue = theDefault;

            System.out.println("Using Environment Variable " + name + " with value " + theValue);
        System.out.println("Using Property " + name + " with value " + theValue);

    return theValue;

This supported my local debugging, CI and console mvn test triggering.

But as with most tasks related to automating systems, I should expect to encounter additional maintenance and workarounds. And such was the case when I tried to execute the tests against BrowserStack in addition to Saucelabs and my local grid.

Basic Saucelabs configuration uses the capabilities:

  • browser
  • platform
  • browser_version

Basic BrowserStack configuration uses the capabilities

  • os
  • os_version
  • browser
  • browser_version

And of course, with BrowserStack we want it to take screenshots to track the @Test method execution so we need to set

  • browserstack.debug=true

Since I have a ‘generic’ grid config in my I wanted to support the extra capabilities in a more generic way.

And I still wanted to allow them to be configured via environment variables or properties.

So, I run through all the environment variables and properties looking for any name prefixed with WEBDRIVER_GRID_CAP_X_

// Allow adding any capability defined as an environment variable
// extra environment capabilities start with "WEBDRIVER_GRID_CAP_X_"

// e.g. WEBDRIVER_GRID_CAP_X_os_version XP
// e.g. WEBDRIVER_GRID_CAP_X_browserstack.debug true
Map<String, String> anyExtraCapabilities = System.getenv();
addAnyValidExtraCapabilityTo(gridCapabilities, anyExtraCapabilities.keySet());

// Now check properties for extra capabilities
Properties anyExtraCapabilityProperties = System.getProperties();
addAnyValidExtraCapabilityTo(gridCapabilities, anyExtraCapabilityProperties.stringPropertyNames());

And the magic addAnyValidExtraCapabilityTo method, looks as follows:

private static void addAnyValidExtraCapabilityTo(DesiredCapabilities gridCapabilities, Set<String> possibleCapabilityKeys) {

    String extraCapabilityPrefix = "WEBDRIVER_GRID_CAP_X_";

    for(String capabilityName : possibleCapabilityKeys){


            String capabilityValue = getPropertyOrEnv(capabilityName, "");

                String capability = capabilityName.replaceFirst(extraCapabilityPrefix,"");
                System.out.println("To Set Capability " + capability + " with value " + capabilityValue);
                gridCapabilities.setCapability(capability, capabilityValue);

Nothing earth shattering there, but it allowed me to add flexibility into the config without adding too much extra code into the class.

If I was doing this in the real world. I would have created a new configuration reader type object, rather than adding the getPropertyOrEnv into the code, but currently, the only config I use relates to the code and I try to avoid too many radical code changes to the course if I can avoid it.

Also, in the real world, you tend not to need to add such a large amount of flexibility. You tend to stick to one grid provider, or refactor your code into more specific abstractions.

So don’t take this code as an exemplar of how to configure your remote driver, instead look upon it as a set of quick, but pragmatic changes to add more flexibility into the code base, and perhaps you’ll see something you can re-use here.

I’ve added additional lectures into my course to explain the config I used for Saucelabs, BrowserStack and TestingBot. And uploaded all the code changes extracted here to the code in the Selenium WebDriver with Java course.

Posted in Grid, Java, Selenium Simplified Blog, WebDriver | 2 Comments

Recent course source code changes for WebDriver 2.46.0

I just finished updating my course source code for WebDriver 2.46.0

I removed some of the code to github:

I had to make a few other minor changes which I summarise below.

Chrome and Firefox

Chrome and ChromeDriver 2.16 now seem to work as well as the Firefox driver. I used to have some issues with Frames and Window management but that has all gone away. Now I have a single set of tests for Chrome and Firefox with no ‘fails on Chrome’ and ‘fails on Firefox’ suites.

I did have to add some additional synchronisation when creating new windows in Firefox. Firefox used to block before returning control to the code when creating new windows, but now it doesn’t so I simple ‘wait’ for the element I need to be available before clicking on it. General synchronisation goodness that we all know and love.


I did have to make changes for cookies though. I assume most people don’t mess much with cookies when using WebDriver, but it is handy functionality when you need it.

ChromeDriver still has a bug where it creates 2 cookies. And now, when ChromeDriver creates a cookie it prefixes the ‘domain’ with a ‘.’. This means you have to be careful working with cookies through ChromeDriver as you might have to work around the browser creating a duplicate cookie after you amend it.

Firefox updates have meant that I now have to append a ‘/’ to my cookie paths.

Minor Things

  • The Select support class gave me a string padded text, so I had to ‘trim’ the output. I suspect this was a browser compatibility thing with my app code.
  • On Mac, my window positioning code didn’t work because I set the Y value to 20, but Mac wanted more space for its top menu bar, so I changed the position to 40.


Posted in Courses, Selenium Simplified Blog, WebDriver | 1 Comment

OperaDriver is now officially deprecated in my code

Anyone remember Opera?

For a while it had the best developer tools and the best JavaScript debugger. It also had ‘good, not great’ WebDriver support, but an easy to add driver via maven.

That stopped when Opera went from version 12.17 and the Opera Driver didn’t work on most recent versions of Opera when they moved to webkit and then blink. But if you still wanted to, you could still use the driver and run tests with the older version of Opera.

But no longer.

The OperaDriver hasn’t been updated so it now compile clashes with Selenium WebDriver 2.46.0

You can exclude the conflicting apache.commons code if you want:

        <!-- changes to Selenium 2.46.0 mean that the operadriver
            now conflicts with Selenium WebDriver. I excluded the
            apache.commons commons-exec from operadriver to allow
            the driver to retain compile time compatibility 
            with webdriver
            <groupId> org.apache.commons</groupId>

That will get you compiled, but won’t help you execute.

In fact OperaDriver hasn’t executed since WebDriver version 2.34.0


The OperaDriver depends on code that moved in WebDriver 2.35.0.

Possibly just as well since Opera always had some issues:

  • Alert Handling
  • Did not refresh reliably
  • Window Management – did not maximise
  • JavaScript Executor and Asyn execution
  • File handling
  • Cookies – did not return the correct expiry date

I have now ‘officially’ removed OperaDriver from my course code base, and moved the basic Opera stuff, just as a legacy example, off to github.

Posted in Selenium Simplified Blog, WebDriver | 1 Comment