Re-using existing browser session with Selenium Grid

In our previous articles we discussed about re-using Browser session in Selenium for local browsers. We spoke about saving the Session ID and the Executor URL for re-creating the sessions. Now let’s us have re-look at the approach when we use Selenium Grid

Launching the Grid

I am using brew to install selenium-server-standalone on my Mac. But if you are using something else, then download the latest version from here

$ selenium-server

For those downloading the jar

$ java -jar selenium-server-standalone-3.4.0.jar

By default the grid launches on port 4444.

Launching the drivers

So let us now launch the driver for Firefox and chrome in Python

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

grid_url = "http://127.0.0.1:4444/wd/hub"

driver_firefox = webdriver.Remote(grid_url, DesiredCapabilities.FIREFOX)
driver_chrome = webdriver.Remote(grid_url, DesiredCapabilities.CHROME)

Now let us print the executor url for one of the drivers

print (driver_firefox.command_executor._url)

This outputs http://127.0.0.1:4444/wd/hub which is nothing but the grid_url that we specified.

In our previous approach we had to save the command executor url to some place and re-use it later to re-create the driver object. But in case of the Selenium Grid the command executor URL is same as the Grid url itself. So no saving needed.

Getting the Session ID

The two key information needed to re-use a Selenium browser session is session_id and command_executor_url, we have the later one already for the grid. Now we need to figure out how to get the session ids.

Luckily the Selenium grid implements a /sessions API endpoint. So if we hit the URL we get below response

$ curl http://127.0.0.1:4444/wd/hub/sessions | jq
{
  "state": "success",
  "sessionId": null,
  "hCode": 973499128,
  "value": [
    {
      "capabilities": {
        "moz:profile": "/var/folders/4k/n292r2rj5_z3cb9ky0vh_szmk_m94b/T/rust_mozprofile.2ntb14zZkPUC",
        "rotatable": false,
        "timeouts": {
          "implicit": 0,
          "pageLoad": 300000,
          "script": 30000
        },
        "pageLoadStrategy": "normal",
        "platform": "ANY",
        "specificationLevel": 0,
        "moz:accessibilityChecks": false,
        "acceptInsecureCerts": true,
        "browserVersion": "53.0.3",
        "platformVersion": "16.6.0",
        "moz:processID": 10299,
        "browserName": "firefox",
        "takesScreenshot": true,
        "javascriptEnabled": true,
        "platformName": "darwin",
        "cssSelectorsEnabled": true
      },
      "id": "26294a77-7ab2-47f1-81fd-e11f593bd960",
      "hCode": 1451216119,
      "class": "org.openqa.selenium.remote.server.handler.GetAllSessions$SessionInfo"
    },
    {
      "capabilities": {
        "applicationCacheEnabled": false,
        "rotatable": false,
        "mobileEmulationEnabled": false,
        "networkConnectionEnabled": true,
        "chrome": {
          "chromedriverVersion": "2.27.440174 (e97a722caafc2d3a8b807ee115bfb307f7d2cfd9)",
          "userDataDir": "/var/folders/4k/n292r2rj5_z3cb9ky0vh_szmk_m94b/T/.org.chromium.Chromium.ZtW0SD"
        },
        "takesHeapSnapshot": true,
        "pageLoadStrategy": "normal",
        "unhandledPromptBehavior": "",
        "databaseEnabled": false,
        "handlesAlerts": true,
        "hasTouchScreen": true,
        "version": "58.0.3029.110",
        "platform": "MAC",
        "browserConnectionEnabled": false,
        "nativeEvents": true,
        "acceptSslCerts": true,
        "locationContextEnabled": true,
        "webStorageEnabled": true,
        "browserName": "chrome",
        "takesScreenshot": true,
        "javascriptEnabled": true,
        "cssSelectorsEnabled": true,
        "unexpectedAlertBehaviour": ""
      },
      "id": "29aa25cb-a60a-4454-a35c-315f76ff1251",
      "hCode": 966845962,
      "class": "org.openqa.selenium.remote.server.handler.GetAllSessions$SessionInfo"
    }
  ],
  "class": "org.openqa.selenium.remote.Response",
  "status": 0
}

We can read these details in python using below code

import urllib.request
import json

grid_url = "http://127.0.0.1:4444/wd/hub"

sessions_req = urllib.request.urlopen(grid_url + "/sessions")
sessions_data = sessions_req.read()
sessions_encoding = sessions_req.info().get_content_charset('utf-8')

sessions = json.loads(sessions_data.decode(sessions_encoding))

for session in sessions["value"]:
	print (session["id"])
	print (session["capabilities"]["browserName"])

The above code prints

26294a77-7ab2-47f1-81fd-e11f593bd960
firefox
29aa25cb-a60a-4454-a35c-315f76ff1251
chrome

So now we have the session id also of our browsers. Using the function the we create in our previous article

def create_driver_session(session_id, executor_url):
    from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver

    # Save the original function, so we can revert our patch
    org_command_execute = RemoteWebDriver.execute

    def new_command_execute(self, command, params=None):
        if command == "newSession":
            # Mock the response
            return {'success': 0, 'value': None, 'sessionId': session_id}
        else:
            return org_command_execute(self, command, params)

    # Patch the function before creating the driver object
    RemoteWebDriver.execute = new_command_execute

    new_driver = webdriver.Remote(command_executor=executor_url, desired_capabilities={})
    new_driver.session_id = session_id

    # Replace the patched function with original function
    RemoteWebDriver.execute = org_command_execute

    return new_driver

Let us recreate these two drivers

driver_firefox_reuse = create_driver_session("26294a77-7ab2-47f1-81fd-e11f593bd960", grid_url)

driver_chrome_reuse = create_driver_session("29aa25cb-a60a-4454-a35c-315f76ff1251", grid_url)

driver_firefox_reuse.get("http://tarunlalwani.in")
driver_chrome_reuse.get("http://tarunlalwani.in")

And both the browsers navigate to the desired location. This approach creates depenceny on using Grid but in return makes re-using browser sessions a piece of cake.