Monday, August 10, 2015

Selenium content supplier

Assuming that you've ever worked with Selenium, you know that to run tests in Chrome / Internet Explorer browser you should supply special so-called standalone server (chromedriver / IEDriverServer), which implements WebDriver wire protocol. When browser updates happen, most likely you'll need to update appropriate drivers as well. If you work with Grid, sometimes you may also need to update appropriate standalone server when new Selenium version appears.

Well, it could be quite bothering to keep external Selenium content always up-to-date. Would be nice to automate this process somehow, right? As you may guessed, this would be our primary goal for today.

Let's start with some introduction first. There're 2 public resources where you can find a list of available items for downloading Selenium content: selenium storage and chromedriver storage. Direct links will move you to the root XML view, which are useless from end-user perspective. But from developers point it's a mine of information. If you look at these XMLs in details, you'll notice that there's a list of Contents nodes, and each of them contains a special Key. You may wonder how could it help us with our main task? In fact, this key is the last part of an end-point for downloading particular content. So if we concatenate a root URL with one of the listed keys, we'll get a full download URL of any available resource. And it means that we can use a simple GET request for retrieving any content we want. Well, it's quite easy task if you know which particular version is newest. But in fact, we don't have such information. Or we do? Let's take a look at our XML again:
As you can see, all the keys are sorted, so if we could parse this XML somehow and retrieve last node's info, it'd resolve our issue with latest version recognition.

Fortunately, we use IntelliJ IDEA, which can generate XSD schema from XML:


And then we can generate source code from XSD schema:


So to get our XML model, we just need to save it somewhere in project, and everything else could be done in several clicks using our favourite IDE.


Well, now we have a model, and it means that we could send a simple GET request to the root URL to retrieve a list of available contents and put everything into newly generated entities. I prefer REST approach, so let's see how we could do that with Jersey client:
First method tries to get XML content and put it into ListBucketResultType.class. Second overloaded method loops through each content node, applies filtering by key and returns last matching value. You may wonder, what Content type is about? It's a custom interface, which is intended to provide access to common Selenium content wrappers: ChromeDriver, IEDriver and SeleniumServer. As we may want to download different resources of different OS types / bitness, it was necessary to make our code generic.
As you can see, there was created some preceded configuration for easier content parsing. Each wrapper contains its own set of characteristics. Let's see how we can use this code with content downloading API:
As you can see, we're passing exact content type we want to download. Next goal is to parse XML and find out latest key using getLatestPath API. Received key should be additionally splitted, as it contains the following format - version/resourceName. Now we're ready to prepare a new saving path according to known info about resource name and output folder. When it's done, we just need to send a GET request to remote end-point and read appropriate response into InputStream. To save received file data, we may want to use Apache IOUtils copy API. The last step is to return a saved file name for further processing.

You may wonder, what kind of processing do we else need? Well, some of available artifacts are zipped. So it'd be nice to perform automatic unzipping when downloading is completed, right? That's why we return a saved file name. To unzip items we may want to use some existing library, like zip4j:
There's nothing specific in this code that needs to be discussed, so I'll leave it here for your own investigation.

Cool, so now we can download and unzip any Selenium content we want. But what's about remote delivery? If we're working with Grid, we may want to update not only a hub VM, but all the nodes as well. For this particular case there was added a server platform, with a simple file upload service:
Note that Java 8 parallel streams allows us to increase performance while files processing. Besides that, there was added unzip functionality as well. Now let's take a look at client API for file uploading:
Here we prepare a MultiPart files content for further sending according to passed list of paths. By default, unzipping feature is enabled. But it's safe, as backend side uses zip extension filtering.

That's it. Let's see some test samples to understand how it's easy to download latest Selenium content using this library:
In a first example we try to download the entire Selenium content and unzip it in the same output folder. Second sample represents how to download particular resources and send them to remote VM.

You may wonder, where do we need to specify ip / port of a remote server. It could be done via the main client class parametrized constructor:
For local files processing you may want to use just a default constructor.

That's all about Selenium content supplier. Hope you'll find it useful for your projects. By the way, you can combine it with EnvironmentWatcher service to implement the following scenario on fly: stop all services -> update Selenium content -> raise up the entire automation staff with new jars / drivers.

You can download sources on GitHub. And related samples as well. Main project is not in official Maven repository yet, so you need to build it by your own. Just use mvn clean install command to generate appropriate artifacts, and then you can add the following dependency to your own projects:

No comments:

Post a Comment