How to Select a Web Element with an id containing ‘.’

TLDR: \.

Lesson learned.

I was working with a site and trying to select an element via its id e.g.

<p id="select.me.by.id">Select Me By My id</p>

Simples thought me:

  • #select.me.by.id

Nope.

So I did what all good Selenium debuggers do… I tried in the browser but Chrome didn’t match it.

Tempted though I was to blame Chrome, I tried with XPath, and that worked fine:

  • //p[@id='select.me.by.id']

After a bit of web searching, the solution was clear.

In CSS I wasn’t saying, find the id select.me.by.id

I was saying find the id select with class me by id which was not at all what I wanted.

Fortunately CSS offers escaping, which I didn’t realise.

Until the app is fixed I can locate the id using CSS with:

  • #select\.me\.by\.id

Posted in FAQ, Selenium Simplified | Leave a comment

How to Debug WebDriver JavascriptExecutor in Java

TLDR; JavascriptExecutor requires us to learn JavaScript and use the Browser Console, and check your locator assumptions using FirePath

A common theme when debugging WebDriver code is the need to jump between breakpoints, evaluate and browser dev tools.

And we shall do exactly that in this ‘how to’, when we look at debugging JavascriptExecutor. This is a hypothetical set of actions built around a sample test I was sent by a blog reader. And I’m walking through a debug process to illustrate some points. It is not the most efficient debug process, but very often, we don’t know what the problem is, so our process isn’t efficient. And when you start working with a ‘block of existing code’ it often is not efficient.

Assume we have this test:

private static WebDriver driver;

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

@Test
public void inputText() throws Exception {
    driver.get("http://compendiumdev.co.uk/selenium/testpages/html5_html_form.html" );

    WebElement textField = driver.findElement(By.name("number_field"));

    writeText(textField, "");
    Assert.assertEquals("", textField.getAttribute("value"));
    writeText(textField, "35");
    Assert.assertEquals("35", textField.getAttribute("value"));

    driver.findElement(By.id("submitButton")).click();
    Assert.assertEquals("35", driver.findElement(By.id("_valuenumber_field")).getText());
}

public void writeText(WebElement textField, String text) {
    ((JavascriptExecutor) driver).executeScript("arguments[0].value='" +
            text + "';", textField);
}

And it doesn’t work. The second Assertfails, so we suspect probably something wrong with the Javascript, but we didn’t get an error, so it isn’t a syntax error.

What do we do?

Well, we could hack about a bit with the writeText method and change the Javascript.

public void writeText(WebElement textField, String text) {
    //((JavascriptExecutor) driver).executeScript("arguments[0].value='" +
    //        text + "';", textField);
    ((JavascriptExecutor) driver).executeScript("arguments[0].setAttribute=('value','" +
                    text + "';", textField);
}

Nope, that gives a syntax error on the JavaScript.

I’ll rewrite that method to make it a little easier to read:

public void writeText(WebElement textField, String text) {
   
    String js;
    JavascriptExecutor exec = (JavascriptExecutor) driver;
   
    //js = String.format("arguments[0].value='%s';",text);
    //exec.executeScript(js, textField);

    js = String.format("arguments[0].setAttribute=('value','%s';",text);
    exec.executeScript(js, textField);
}

Note: this is now much easier for me to breakpoint, debug and evaluate, but I don’t think I’ll learn much here by doing that.

And the code is now more readable and it is obvious what the syntax error is, I missed off a ‘)’.

So I’ll fix the syntax error. And…

js = String.format("arguments[0].setAttribute=('value','%s');",text);

Yeah, and that’s not how you call setAttribute…

OK. Time to stop hacking about in Java and start debugging.

First use the console

First I’m going to execute the JavaScript in the console.

That means rewriting it.

I find the element using By.name in Java, so I’ll use the console and type:

document.getElementsByName("number_field")[0]

That is equivalent to the findElement(By.name("number_field"))

And it shows me:

<input type="number" value="12" name="number_field">

Which is the same as the input field on screen which defaults to 12.

So I’ll set the value using setAttribute

document.getElementsByName("number_field")[0].setAttribute('value','13');

Then check that the attribute is set

document.getElementsByName("number_field ")[0].value

And the console tells me “13” but the GUI stills says “12”.

Hmmm…

Thinking that I should use ‘value’ instead, just in case, I’ll try.

document.getElementsByName("number_field")[0].value='14';

And then check it was set:

document.getElementsByName("number_field ")[0].value

And the console tells me “14” but the GUI stills says “12”.

OK.

Let me check first assumptions and check the locator.

Using FirePath in Firefox:

[name="number_field"]

Oh. FirePath is showing me two fields with that name, both with a default value of ‘12’.

And the one I want is the second one.

Let me just try that JavaScript again.

document.getElementsByName("number_field")[1].value='15';

And that changed it on the GUI as well.

So I’ve found my problem.

Model the console JavaScript in the Java Code

First I’ll change the JavaScript method to be equivalent to what I did above.

public void writeText(WebElement textField, String text) {

    String js;
    JavascriptExecutor exec = (JavascriptExecutor) driver;

    js = String.format("arguments[0].value='%s';",text);
    exec.executeScript(js, textField);
}

And then I’ll change how I locate the element:

WebElement textField = driver.findElements(By.name("number_field")).get(1);

Which is actually pretty close to what I wrote in JavaScript.

And try the @Test

And it worked.

At this point I might want to try and refactor the locator to avoid a findElements but if that isn’t possible, I’ll leave it as it is. (for now).

But I’ll add a comment:

/* there are two inputs named “number_field” the first in a non-displayed, prototype form */

And either amend the web page or raise a bug against the system.

We were slightly unlucky that the non-displayed form had the same value in the “number_field: as the main form. Otherwise an early assertion on the value of the found element might have revealed the problem early in the code. But my real problem stemmed from inspecting the element, but not running the locator through FirePath. I didn’t test the locator early enough.

Summary

When working with JavascriptExecutor:

  • Write the JavaScript code with String.format because string concatenation makes it hard to read
  • Run your code in the console (you might have to amend it a bit)
  • Check results in the console

When working with WebDriver:

  • Check your assumptions (locators) by running the same locator in FirePath or Chrome ‘find’

When coding, if you aren’t writing Unit tests (and very often when we work with WebDriver we don’t):

  • Run your code as early as you can, to avoid writing a lot of code that you have to debug
  • Write code simply, e.g. inline, then ‘extract to method’ when it is working
  • Assert often, you can always remove redundant and irrelevant asserts early

 

Posted in WebDriver | Leave a comment

How to debug chained WebDriver `findElement` methods in Java

TLDR; split code across lines and breakpoint, and use browser dev tools to test the locators used.

Have you ever had an argument about fluent code? Discussions about “train wrecks” and “impossible to debug”?

WebDriver has the ability to chain findElement calls, and create such “train wrecks”. How do you debug it if you use it? (There is a summary video at the bottom of this post.)

Example:

WebElement element =
driver.findElement(By.id("a8")).findElement(By.name("pName8")).findElement(By.tagName("a"));

Each findElement returns a WebElement. Each WebElement has a findElement method. So we can chain them.

This allows us to find an element, then find a child of that element, and then find a child of that element.

Why?

That’s not really important right now since I’m discussing debugging, but …

  • might lead to easier to read code that a complicated CSS or XPath
  • we could store the results in variables and that might be easier to maintain
  • because… options. And having options is important.

But if something goes wrong… train wreck

“If something goes wrong it is impossible to debug.”

So I’ve been told.

But it doesn’t have to be impossible. Let’s consider some strategies.

Strategies

Intermediate Variables

If the WebElement returned from each call to findElement was stored in a variable then we could breakpoint each line, and add a ‘watch’ for each variable.

WebElement anchor = driver.findElement(By.id("a8"));
WebElement paragraph = anchor.findElement(By.name("pName8"));
WebElement finalAnchor = paragraph.findElement(By.tagName("a"));

This might also make it easier to see that we are looking for: anchor > paragraph > finalAnchor.

New Lines

What?

Yup, add a new line in the code so it looks like this:

WebElement element = driver.findElement(By.id("a8")).
                    findElement(By.name("pName8")).
                    findElement(By.tagName("a"));

Then we can add a breakpoint to each line.

You don’t get the benefit of being able to watch the early calls in the chain, but you can always use evaluate to run the code when breakpointed.

What about debugging locators?

And suppose we breakpoint it, and can see what goes wrong. How do we know what to change it to?

When debugging locators and location strategies we want to do that interactively in the browser. I tend to use the Firebug FirePath plugin, but we have similar features in Chrome and Firefox Browser Dev Tools.

e.g. in Chrome I can right click, ‘inspect’ and in the ‘Elements’ tab, can use find (ctrl+f) to search using Xpath, or CSS selectors.

@Test
public void findElementChaining(){
    WebDriver driver = new FirefoxDriver();

    driver.navigate().
    to("http://www.compendiumdev.co.uk/selenium/find_by_playground.php");
    
    WebElement element = driver.findElement(By.id("a8")).
                        findElement(By.name("pName8")).
                        findElement(By.tagName("a"));
    
    Assert.assertEquals("This is h paragraph text", element.getText());
}

And if I try the selectors in the example code above I can see that #a8 matches an anchor, which has no children. I can see that the chained By.name ([name="pName8"]) will never match anything, since it is the same element as the previous id locator.

Summary

  • It is important to have options with the code you write.
  • Learn to use the Evaluate Tool in IntelliJ
  • Split ‘chained’ method calls across new lines to add breakpoints
  • Using intermediate variables might make code easier to read
  • Test locators in the browser interactively using Browser Dev Tools

Posted in WebDriver | 2 Comments

Selenium WebDriver 3.0.0-beta2 and JUnit and GeckoDriver v 0.10.0

I started to have a look at `Selenium WebDriver 3.0.0-beta2`

And my `@Test` methods were running from the IDE, but not running on CI.

The effect in CI was, no tests executing at all:

 
 Running TestSuite
 Configuring TestNG with: org.apache.maven.surefire.testng.conf.TestNG652Configurator@77468bd
 Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.424 sec

I don’t use TestNG, I use JUnit.

Since this only happened when I switched to 3.0.0-beta2, and not in beta1 or 2.53.1, I raised a defect on github.

Luke pointed out that we can use the dependency management in Maven to exclude some of the dependencies.

Making my maven include for Selenium WebDriver 3.0.0-beta2 the following:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-server</artifactId>
    <version>3.0.0-beta2</version>
    <exclusions>
        <exclusion>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
        </exclusion>
    </exclusions>
</dependency>

So I changed the `pom.xml` make to my CI project to run beta2

I also upgraded the Marionette/Geckodriver executable.

David Burns has released a v 0.10.0 version of GeckoDriver to use with the v 3.0.0-betas

  • https://github.com/mozilla/geckodriver/releases

So upgrade to that when working with 3.0.0-betaX

 

 

 

 

Posted in WebDriver | Leave a comment

WebDriver Windows 10 Anniversary Build and Edge 14393

TLDR; if your version of Edge is not listed on the WebDriver Edge page, try the insider build.

I am experimenting with Selenium WebDriver 3.0.0 beta2 at the moment – more on that later. And this morning I could not get MS Edge to work. I could not understand how the beta was affecting Edge.

And then I remembered that I upgraded to Windows 10 Anniversary last night so now Edge is on a new version:

  • Microsoft Edge 38.14393.0.0
  • Microsoft EdgeHTML 14.14393

A quick look at the Microsoft WebDriver Edge page reveals no listed version for 14393.

Where best to search for “WebDriver Edge 14393″?

I thought Bing might work best for MS

And lo, there was a clue in the second link.

” I was able to make selection through Webdriver (14366) on Windows Insider(14393)…”

14393 is mentioned as an “Insider” build, even though I think it is not now.

But I tried that and it worked.

So for the moment – if you are on 14393 of Edge and it still isn’t listed on the Microsoft WebDriver Edge page  then try the insider build.

P.S. You might also want to double check your IE Security Zones, because mine were no longer set to the same protection setting.

  • IE -> Settings -> Internet Options -> Security
    • then set all zones to same protected mode

 

Posted in WebDriver | Leave a comment

Question: What is the best Page Object framework for Java?

TLDR; Q: what is the best Page Object framework? A: I don’t know. I don’t use any

Question: What is the best Page Object framework for Java? And are there any tools to build Page Objects e.g. scan the page and create a class with all the @FindBy annotations on WebElements etc.

Answer:

I don’t know. I have not researched any Page Object frameworks. I don’t use any, and don’t recommend any.

I know there are plenty of libraries for other languages

Ruby:

Python:

And there are bound to be more. I don’t know.

I prefer to:

  • write the classes I need by hand,
  • as I need them,
  • adding methods and WebElements as I use them
  • refactoring the classes as they, and my understanding, and our use of them, evolve

I don’t even recommend using the Page Factory built in to Selenium WebDriver e.g. @FindBy’s etc. because every time I’ve used it, I have eventually moved away from those annotations and the PageFactory.initElements to allow more flexible synchronisation and more flexibility in general.

But note the “every time I’ve used it”. Because…

There was a time when I did want a framework to make it easy.

And there was a time where I not only wanted a tool to scan the page and build a class, but I actually thought about building a tool which would scan the page and build the class, and I think I created some prototype code, but I can’t find it now.

But that was when I viewed writing the Page Objects as drudge work. Work that added no value to the automating.

In fact. Every class we add has a reason. The classes represent a model of my understanding of the application and how we would go about automating it.

I think being aware of that allows me to build cleaner and simpler code.

Automatically building Page Objects would have led to ‘large’ Page Objects which would be unmaintainable [“Ah, but I wouldn’t have to maintain them because they would be automatically created” :) ]

They would also be hard to use. Imagine code completion on an automatically created Page Object, there would be no grouping of elements into headers and footers, or identified widgets, there would just be a big list of stuff – and would the automatically generated names read well enough to explain what they were for?

I can understand why such a tool or framework would look attractive, but longer term I think it would hamper, rather than improve, productivity.

It might even prevent you from experimenting.

In the Page Objects and Beyond tutorial and talk, I show code which has many different approaches to abstraction for Page Objects:

  • Element abstractions
  • Physical abstractions (which are analogous to automatically generated abstractions but with full human control)
  • Logical Abstractions
  • Slow Loadable Abstractions
  • Navigation Abstractions

And there are more. You would miss out on Actor/Task type approaches like screenplay pattern.

I have a general aversion to frameworks.

I don’t mind libraries, but anything that controls the way I write the code, I try to minimize my exposure to.

Because I’ve learned to value flexibility.

I know the code is going to evolve, because I know my understanding will evolve. And my understanding is encoded into the abstractions I write.

I know the application is going to evolve. And I want my code to adapt gracefully as the application changes.

I know I’m going to refactoring my code, to improve the structure and implementation.

I don’t want anything that might get in the way of my doing that.

It could be that there is a perfect framework out there for me, and because I’m not looking for it, I’ll never find it.

But I haven’t needed it.

PS. Having said all that, if you find one that works well then let me know.

Posted in WebDriver | Leave a comment

First impressions of Selenium WebDriver 3.0.0 beta 1

TLDR; Selenium 3 WebDriver is pretty much a drop in replacement for Selenium 2 WebDriver

The 3.0.0 Beta v1 version of WebDriver has been released and my first Impressions of Selenium 3 beta are very good.

And first impressions means, can I run the stuff I have already.

I amended the pom.xml to bring the release down from maven central.

<!-- <webdriver.version>2.53.1</webdriver.version> -->
<webdriver.version>3.0.0-beta1</webdriver.version>

So I effectively included it as

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-server</artifactId>
    <version>3.0.0-beta1</version>
</dependency>

Since WebDriver now needs Java 1.8 so I’ll bump the language dependencies in pom.xml

<configuration>
   source>1.8</source>
   <target>1.8</target>
</configuration>

In my test code I’ve been using a base64 encoding implementation bundled with Selenium, for working with screenshots

//import org.bouncycastle.util.encoders.Base64;

Since WebDriver now needs Java 1.8, I’ll just use the decoder in Java 1.8 instead

//Base64 decoder = new Base64();
//byte[] imgBytes = (byte[]) decoder.decode(tempImageFileAsBase64);
byte[] imgBytes = Base64.getDecoder().decode(tempImageFileAsBase64);

With WebDriver 3.0.0-beta1 on Windows 10

  • FirefoxDriver && Firefox 47.0.1 – all tests passed
  • ChromeDriver v2.22.397933 && Chrome Version 52.0.2743.82 m – all tests passed
  • IEDriverServer (32 bit) v2.53.1 && IE 11.494.10586- 1 test failed
  • HTMLUnit 2.21 – 28 tests failed
  • Marionette v 0.9.0 (64 bit)&& Firefox 47.0.1 – 65 tests failed
  • Edge v 10586- 70 tests failed

3.0.0-beta1 defaults to Marionette, so to use the internal FirefoxDriver we can

  • set a capability
DesiredCapabilities legacyCapabilities = DesiredCapabilities.firefox();
legacyCapabilities.setCapability("marionette", false);
WebDriver aDriver = new FirefoxDriver(legacyCapabilities);

Code suggests that the plan is to allow us to:

  • set a system property “webdriver.firefox.marionette”

But I couldn’t get that to work:

  • -Dwebdriver.firefox.marionette=false
  • System.setProperty(FirefoxDriver.SystemProperty.DRIVER_USE_MARIONETTE, "false");

It doesn’t look like that has been coded for yet. Although I can now see that this was present in 2.53.1 as well (I didn’t notice that before)(I couldn’t get it to work in 2.53.1 either).

So – set the capability for now to switch between Marionette and legacy driver in Selenium WebDriver 3.0.0-beta1.

The MarionetteDriver that I’ve been using will be deprecated and we’ll use the FirefoxDriver for both Marionette and Legacy – with just a toggle of the capability.

And with that I guess I’m ready to migrate to 3.0.0 when it moves out of beta.

Pretty much the drop in replacement that the selenium team have been working towards. Another A+

Posted in WebDriver | Leave a comment

Comparing WebDriver 2.53.1 execution for Firefox, Chrome, IE, HTMLUnit, Edge and Marionette

The Selenium 2 WebDriver with Java course has a new Continuous Integration section. Instead of using SVN we now use Git and have the source for the CI experimentation in Git (more info on this update).

As part of this process I ran all the tests on all current versions of browsers to see the status. I last performed this type of retrospective back on (WebDriver 2.42.2](http://seleniumsimplified.com/2014/06/a-quick-webdriver-2-42-2-retrospective/)

And boy have things changed with WebDriver 2.53.1.

First IE. Amazing.

I used WebDriver 2.53.1 && IEDriverServer (32 bit) v2.53.1 and only 1 of my @Test methods failed. Very impressed. It might even be an incompatibility between the HTML I use in the test page with IE rather than a driver issue, I still have to check that. Most impressed indeed.

FirefoxDriver and ChromeDriver were at parity – all @Test methods passed on both.

HTMLUnit 2.21 surprised me, and

With WebDriver 2.53.1 on Windows 10

  • FirefoxDriver && Firefox 47.0.1 – all tests passed
  • ChromeDriver v2.22.397933 && Chrome Version 52.0.2743.82 m – all tests passed
  • IEDriverServer (32 bit) v2.53.1 && IE 11.494.10586- 1 test failed
  • HTMLUnit 2.21 – 30 tests failed
  • Marionette v 0.9.0 (64 bit) && Firefox 47.0.1 – 66 tests failed
  • Edge v 10586- 71 tests failed

The HTMLUnit results could have been slightly better. HTMLUnit has a bug in version 2.21 where it can’t getText() from paragraphs where the ‘text’ is a ‘sibling’ to an element e.g.

<p id="p2" name="pName2" >
    <a name="pName2" id="a2">
    </a>
    This is b paragraph text
</p>

Without the embedded anchor tag – HTMLUnit can getText perfectly but my test pages and code are often designed to hit ‘edge’ cases or ‘hard to automate’ code.

HTMLUnit 2.22 does not have this problem. But HTMLUnit 2.22 failed its last release build on travis-ci.

I know the problem is fixed in 2.22 because I downloaded the code to see if I could fix the error and on creating a unit test to trigger the problem, the problem did not occur. So I look forward to a release of HTMLUnit 2.22 in the future. But still a good showing from HTMLUnit, much better than I expected.

Edge and Marionette are still works in progress and some of the issues are simply ‘we haven’t implemented this yet’. Edge seems very fast in operation.

So all told, a very impressive showing, and an “A+” all round I think.

Posted in WebDriver | Leave a comment

Using Intellij to debug @Test with the ‘Watch’ function

I was sent a query about why a cookies test was failing on Chrome but not on Firefox.

The debug process involved me using techniques that I don’t think I have blogged about or demonstrated before so I repeated the debug session for a video and have written it up here.

Essence:

  • Run test on Firefox to make sure it does work
  • Change Run configuration in IntelliJ to configure Chrome as the browser
  • Breakpoints on relevant lines
  • Add a watch that uses ‘code’ not just a variable name
  • Step through the code comparing my expectations with the actual values shown in the variables and watch view

I have a `Driver` class, which allows me to configure which driver I use by a method call, or environment variables or properties.

The fact that it uses properties means that if I have a run configuration in IntelliJ I can add a `-D` parameter to the VM options to configure my browser

  • `-Dselenium2basics.webdriver=CHROME`

The ‘watch’ functionality in the debug view can be used to watch variables, but it can also be used to ‘execute arbitrary code and watch the results’ after each ‘step’ in the debug process.

I setup a ‘watch’ on `driver.manage().getCookies()` to see the cookies retrieved and set by Selenium WebDriver on each step.

By comparing the cookies that I created, with the cookies that were added, I could see that the cookie created by `driver.manage().addCookie` through the ChromeDriver added a `.` before the domain in the cookie.

i.e. I created a cookie with domain `compendiumdev.co.uk` it was added to the brower as `.compendiumdev.co.uk`

This led to the side-effect that:

  • the JavaScript on the page could access the cookie fine
  • WebDriver could get the cookie just fine
  • when the JavaScript on the page amended the cookie, it created a new cookie

I have to perform a follow on action to see:

  • if I need to fix the code on my test page, since the JavaScript might not be ‘amending the cookie properly’ or it might have a bug deleting the cookie, etc. or
  • if it is a bug in ChromeDriver (different effect between FirefoxDriver and ChromeDriver suggests it might be a bug)

I don’t know yet. But the debug process identified the cause of the side-effect, now I have to work on the fix.

You can see the debug session in the video below, and the two blog posts listed below might be useful if you are debugging Selenium WebDriver and Java @Test code:


P.S. I decided to absorb the ‘fix’into my test pages, so that the cookie deletion code doesn’t just expire it for the current domain, it also expires it for  `”.” + document.domain`

 document.cookie = name + "=" + cval + "; expires=" + exp.toGMTString();
 document.cookie = name + "=" + cval + "; domain=." + document.domain + "; expires=" + exp.toGMTString();
Posted in Java, WebDriver | Leave a comment

Appium – a very short getting started guide

I recently amended the Selenium WebDriver API with Java online training to include a section on Appium with Android.

In essence, Appium for mobile web testing is pretty simple, you simply use RemoteWebDriver to connect to the Appium server.

Use the capabilities to setup the remote webdriver.

DesiredCapabilities capabilities = new DesiredCapabilities();

capabilities.setCapability("deviceName", "emulator-5554");
capabilities.setCapability("platformName", Platform.ANDROID);
// if you are using an up to date version of android to you can use "Chrome" as the app,
// otherwise "browser" is the default 'browser' on Android
capabilities.setCapability("app", "browser");

String appiumURL = "http://127.0.0.1:4723/wd/hub";
driver = new RemoteWebDriver(
        new URL(appiumURL),
        capabilities);

// and then use driver as normal

To get started with Appium:

If you are working with modern physical device connected via USB then you can probably use "Chrome" as your "app" and then drive the Mobile Chrome browser (which handles frames better than the default ‘browser’.

If you are working with the Android VM and an AVD then you’ll probably have to use Level 23 of the API, which will come with ‘browser’ rather than ‘chrome’.

Android N (api level 24) comes with Chrome in the AVD but you’ll have to amend Appium slightly to work with it (I chose not to do that and used level 23 with the AVD, and used Chrome on the physical devices).

We cover all of this in more detail on the course but each of the sites linked to above, has install and getting started instructions.

And if you get everything setup, just connect as a RemoteWebDriver as shown in the code snippet above.

You might have to amend the @Test code a little to introduce different synchronisation, but I was surprised at how compatible it was.

Appium has come a long way in a short time, has overcome enormous challenges and deserves to be the default mobile bridge for Selenium WebDriver.

Posted in WebDriver | Leave a comment