alxgrk / spring-selenium-pool

A library that provides a pool of dockerized Selenium instances.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

🏊 Selenium Pool

Maven Central DEV.to Post

This small library helps to create a pool of Selenium Docker Container with a configurable size.

🩲 Prerequisites

Make sure to have the following installed:

  • Docker

πŸ’Ώ Installation

For Maven, add this dependency:

<dependency>
  <groupId>de.alxgrk</groupId>
  <artifactId>spring-selenium-pool-core</artifactId>
  <version>1.0.0</version>
</dependency>

For Gradle use:

implementation("de.alxgrk:spring-selenium-pool-core:1.0.0")

πŸ› οΈ Configuration

This library is configurable via Spring-Boot properties. Auto-Completion when working with IntelliJ included. This is an example configuration showing the default values:

# default values
selenium.pool.size=3
selenium.pool.recording-directory= # if empty, no recording will happen
selenium.pool.profiles-directory= # if empty, a temporary directory will be created and deleted on exit
selenium.pool.extension-files-in-classpath= # if empty, no extension files will be loaded

πŸ₯½ Utilities

To connect to the running Selenium container, you have to have vncviewer & tigervnc-common installed. If correctly configured, simply run:

./vnc.sh

🀽 Usage

πŸ’† Getting a container

Trying to get a container synchronously returns null, if there is no idle container at the moment.

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        val container: WebDriverForContainer? = webDriverPool.getWebDriverForContainer()
    }    

}

Instead, one could also use the *Async method, to wait until there is an idle container. (If you've expected a suspending function at this point, have a look at CompletionStage.await() extension function for converting CompletableFuture)

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        val container: CompletableFuture<WebDriverForContainer> = webDriverPool.getWebDriverForContainerAsync()
        
        // blocking get
        container.get()

        // ... or with timeout
        container.get(1, TimeUnit.SECONDS)

        // ... or coroutine style
        GlobalScope.launch {
            val webDriverForContainer = container.await()
        }
    }    

}

If you used a container before, you should have a ContainerId, which can be used to get the formerly used container again.

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        val container = webDriverPool.getWebDriverForContainer()

        // do something...

        val theSameContainer = webDriverPool.getWebDriverForContainer(container.containerId)
    }    

}

Since you might want to reuse a formerly used Chrome profile (in order to still have access to cookies etc.), it is possible to hand over the name of this profile.

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        val container = webDriverPool.getWebDriverForContainer(profile = ChromeProfile("username"))
        // cookies etc is stored from last use
    }    

}

🀝 Interacting with Selenium

To interact with the Selenium server running within the container, you can simply use the provided WebDriver instance.

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        val container = webDriverPool.getWebDriverForContainer()
        container!!.webDriver.get("https://foo.bar/")
    }    

}

πŸ‘‹ Finishing work

Once you are done with doing whatever you wanted to do, you should return the container to the pool (unless you are sure, you'll need the exact same instance later).

Since WebDriverForContainer implements AutoClosable, you can simply use try-with-resources in Java or .use() extension function in Kotlin.

@Component
class SomeSeleniumTask(@Autowired webDriverPool: WebDriverPool) {

    init {
        webDriverPool.getWebDriverForContainer()!!.use {
            it.webDriver.get("https://foo.bar/")
        }
    }    

}

✨ Custom Chrome extension

If you read about the configuration properties carefully, you may have notice the parameter about extension files. It is possible to install a custom Chrome extension by specifying a manifest.json and a corresponding script.js file. This might be helpful for a couple of reasons including e.g. for grabbing the network traffic. For an example on how to use this, see spring-selenium-pool-example.

πŸŽ‰ Acknowledgements

This library heavily relies on the great work of the Testcontainers team - thanks a lot!

πŸ“ Closing notes

I hope you like this small library. If you have any feedback or encounter any problem, feel free to contact me, create an issue or even better a pull request.

About

A library that provides a pool of dockerized Selenium instances.

License:Apache License 2.0


Languages

Language:Kotlin 97.2%Language:JavaScript 1.5%Language:Shell 1.3%