1

I have been developing a browser automation application in nwjs/node.js that uses a data driven DSL of instructions.

I decided to use the npm packages for chromedriver and selenium webdriver via the following dependencies in my package.json:

  "dependencies": {
    "selenium-webdriver": "~3.0.0-beta-2",
    "chromedriver": "~2.23.1"
  }

Everything has been going really well until recently the application stopped working at the point of simply launching the chromedriver:

var webdriver = require("selenium-webdriver");
var driver = new webdriver.Builder().forBrowser('chrome').build();

The following is the error when running in the terminal on my Macbook:

/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/promise.js:654
    throw error;
    ^

SessionNotCreatedError: session not created exception
from unknown error: Runtime.evaluate missing 'wasThrown'
  (Session info: chrome=54.0.2840.71)
  (Driver info: chromedriver=2.23.409710 (0c4084804897ac45b5ff65a690ec6583b97225c0),platform=Mac OS X 10.11.5 x86_64)
    at WebDriverError (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/error.js:27:5)
    at SessionNotCreatedError (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/error.js:214:5)
    at Object.checkLegacyResponse (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/error.js:505:15)
    at parseHttpResponse (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/http.js:396:13)
    at doSend.then.response (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/http.js:328:11)
    at process._tickCallback (internal/process/next_tick.js:103:7)
From: Task: WebDriver.createSession()
    at Function.createSession (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/webdriver.js:366:24)
    at Driver (/Users/matthewsanders/test1/node_modules/selenium-webdriver/chrome.js:804:38)
    at Builder.build (/Users/matthewsanders/test1/node_modules/selenium-webdriver/index.js:546:16)
    at Object.<anonymous> (/Users/matthewsanders/test1/index.js:4:59)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.runMain (module.js:590:10)
From: Task: WebDriver.navigate().to(http://www.google.com)
    at Driver.schedule (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/webdriver.js:414:17)
    at Navigation.to (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/webdriver.js:1042:25)
    at Driver.get (/Users/matthewsanders/test1/node_modules/selenium-webdriver/lib/webdriver.js:832:28)
    at Object.<anonymous> (/Users/matthewsanders/test1/index.js:6:8)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.runMain (module.js:590:10)

I tested against several test applications I had made at the time as well to make sure it wasn't an issue I had introduced in the project.

I then tried to use 'firefox' in the forBrowser method after downloading the geckodriver and this worked perfectly.

It would appear as though some sort of auto update of the chromedriver has broken my project. I had noticed several other questions on stackoverflow related to similar errors, although none of them included the Runtime.evaluate missing 'wasThrown' error, that seem to suggest my theory is probably correct.

I COULD just go ahead and switch to geckodriver, but I'm not sure if I will run into a similar issue down the road.

I guess the real question here is...

How can I disable auto updates to keep this application running as a stand alone project?

Again this is not intended as a constantly up to date test suite, but rather a browser automation application driven by a custom language. It operates as both an automated solution via a hosted node.js instance or as an operator driven gui application running with nwjs.

EDIT:

So far I have found a promising approach. If I get it working to my liking I will add an answer.

The Selenium Documentation has information on how to customize the chromedriver options. I have downloaded a version of Chromium and used this logic to hook it up:

var chrome = require("selenium-webdriver/chrome");
var service = new chrome.ServiceBuilder().build();

var options = new chrome.Options();
options.setChromeBinaryPath("PATH/TO/MY/CHROMIUM");

var driver = new chrome.Driver(options, service);

Note: on mac you must provide a path the the actual executable inside the app package ex: 'Chromium.app/Contents/MacOS/Chromium'.

So far I am using Chromium not Chrome and the version is still not correct, but I did verify it was different than what I am running without setting the path. In theory I should be able to run Chrome the same way if I could reliably freeze the auto update process while still packaging the application stand alone. I may just resort to using Chromium if that doesn't work.

Matthew Sanders
  • 4,875
  • 26
  • 45
  • I seems that you simply need to update chromedriver to match the updated Chrome. The latest version is 2.25 and yours is 2.23. – Florent B. Oct 27 '16 at 15:37
  • Thanks for the reply. That is EXACTLY what I want to avoid. Is chromedriver just using my native chrome? Can I point it to a set version? I need a "set it and forget it" setup here as this will be packaged as an application and shouldn't require updates unless the DSL changes. – Matthew Sanders Oct 27 '16 at 15:40
  • There is another question that seems to suggest I may be able to do what I want [here](http://stackoverflow.com/questions/31783481/how-can-i-downgrade-to-chrome-42-0-2311-90) – Matthew Sanders Oct 27 '16 at 15:43
  • You could update the `ChromeDriver` just this time to match your current version of Chrome (since I know of no way to be able to downgrade the Chrome, or get an older installer) and afterwards prevent Chrome from upgrading. You could do this by going to Chrome installation folder and simply delete the Update folder. – Cosmin Oct 27 '16 at 15:43
  • Yes... but how do I package ALL dependencies so this doesn't happen for the next person to use the app on their system? – Matthew Sanders Oct 27 '16 at 15:45
  • Try to set the version to `latest` in the dependencies: `"dependencies": { "selenium-webdriver": "latest", "chromedriver": "latest" }` – Florent B. Oct 27 '16 at 15:48
  • Thanks for the suggestion, but that is actually worse as then I wouldn't be using a known version of webdriver or chromedriver let alone Chrome. I actually intend on solidifying the version to a very specific version when I do get it ready to package. It sounds like chromedriver relies on Chrome? I was assuming it **WAS** a custom build of chrome. If it is a custom build of chrome perhaps I just have to configure it to not update same as I would chrome. – Matthew Sanders Oct 27 '16 at 15:55
  • The app `chromedriver` is just an interface to communicate with the Chrome browser installed by the user. It's not a custom build of Chrome. – Florent B. Oct 27 '16 at 16:07
  • That is exactly what I needed to know :). So is it possible to download a specific version of chrome, disable updates, and point chromedriver to that instance? EDIT: looks like this may work: https://sites.google.com/a/chromium.org/chromedriver/capabilities – Matthew Sanders Oct 27 '16 at 16:08
  • Search for a standalone/portable Chrome. The CEF (Chromium Embedded Framework) might be what you are looking for. There's a package on npm: https://www.npmjs.com/package/chromium – Florent B. Oct 27 '16 at 16:15
  • just turn off chrome automatic updates, and manage your own upgrades. done. – ddavison Oct 27 '16 at 18:16

4 Answers4

2

Another solution, very close to what @Matthew Sanders suggested is to use Chromium with the following setup

var webdriver = require("selenium-webdriver");
var chrome = require("selenium-webdriver/chrome");

var options = new chrome.Options().setChromeBinaryPath("/usr/bin/chromium-browser")
// options.headless()
var driver = new webdriver.Builder()
    .forBrowser('chrome')
    .setChromeOptions(options)
    .build();
Levon
  • 10,408
  • 4
  • 47
  • 42
1

You can use docker together with the selenium hub to setup an isolated environment that's independent from your installed browsers. There are different browsers for the hub, but as you used chrome before, i would recommend the following:

https://hub.docker.com/r/selenium/standalone-chrome/

After you have installed docker, you can start up the server with

docker run -d -p 4444:4444 selenium/standalone-chrome:latest

You then can connect to it through port 4444.

When everything is up and running, make sure your webdriver uses the remote selenium hub by simply telling it in your code:

var webdriver = require("selenium-webdriver");
var driver = new webdriver.Builder()
    .forBrowser('chrome')
    .usingServer('http://localhost:4444/wd/hub')
    .build();

edit:

As @sircapsalot pointed out you can specify your versions by building the image yourself https://github.com/SeleniumHQ/docker-selenium/wiki/Building-your-own-images#specify-chromedriver-and-chrome-versions

Johannes Merz
  • 3,252
  • 17
  • 33
  • Good to know this option exists. In this particular client application I didn't want the added complexity of adding docker to the mix, but I could see the utility in other applications. I have found a solution at this point and will post my answer as well. – Matthew Sanders Oct 27 '16 at 19:36
  • i'm the maintainer of these docker containers - i've also created this [wiki page](https://github.com/SeleniumHQ/docker-selenium/wiki/Building-your-own-images#specify-chromedriver-and-chrome-versions) that tells how to manage specific versions of browsers with their driver counterparts. – ddavison Oct 27 '16 at 19:42
0

How to use Selenium chromedriver without being forced to update?

Simple Answer:

Disable your automatic browser updates.

By letting automatic updates happen, you run into the chance of having disparate versions of Chrome with Chromedriver. The same will happen with Firefox and Geckodriver.

It is better to have a more controlled process of your upgrades. When you are ready to update, update manually to newest chrome and chromedriver version.

ddavison
  • 28,221
  • 15
  • 85
  • 110
  • Not that simple, however, that is the idea I am looking into. I need to be able to package a specific binary of chrome with my application. Most of what I have seen currently suggests installing chrome on different user accounts, however, I don't have that ability in this case either. The closest thing is chromium or CEF. – Matthew Sanders Oct 27 '16 at 18:20
  • `"not that simple"` do you not have access to change automatic updates of your browser? why not use our [docker containers](https://github.com/seleniumhq/docker-selenium)? you can manage [specific versions](https://github.com/SeleniumHQ/docker-selenium/wiki/Building-your-own-images#specify-chromedriver-and-chrome-versions) in those – ddavison Oct 27 '16 at 18:21
  • I have access on my own system sure... but I need to package the browser **with** my application. I don't want to mess with an existing browser on the users' systems. The actual application is already browser based as well since it is using CEF, but due to limitations with Selenium it must launch a separate browser for doing the automation. That second browser is currently chrome driven by the chromedriver. – Matthew Sanders Oct 27 '16 at 18:24
  • I'm sure I can download [chromium](https://www.chromium.org/getting-involved/download-chromium) as another option, however, I know that it is a slightly different build then Chrome proper. So I was hoping to get Chrome minus the updates and bundle that portably with my application. – Matthew Sanders Oct 27 '16 at 18:28
0

I was able to solve my problem by using Chromium, however, I did test to make sure it would work with Chrome as well. The problem with Chrome is that you can't easily find older releases and they can (and do) change the way you disable auto-updates with more recent versions. There are several suggestions out there for disabling auto updates in Chrome, some are more valid than others.

I decided to download a previous version of Chromium following the guidelines on the website (in this case it uses version 44 as an example):

  1. Look in http://googlechromereleases.blogspot.com/search/label/Stable%20updates for the last time "44." was mentioned.
  2. Loop up that version history ("44.0.2403.157") in the Position Lookup
  3. In this case it returns a base position of "330231". This is the commit of where the 44 release was branched, back in May 2015.*
  4. Open the continuous builds archive
  5. Click through on your platform (Linux/Mac/Win)
  6. Paste "330231" into the filter field at the top and wait for all the results to XHR in.

Note: that sometimes the number will not match and you have to scroll through the list to the next lowest match you see. I had to go back several matches to find a working copy in my situation.

Once you have a binary here is the javascript logic to use it ( More info can be found in the Selenium Docs ):

// get the webdriver for future operations
var webdriver = require("selenium-webdriver");
// configure the chromedriver
var chrome = require("selenium-webdriver/chrome");
// here I get the path of chromedriver to avoid using path variables
var path = new require("chromedriver").path;
var service = new chrome.ServiceBuilder(path).build();

var options = new chrome.Options();
// Note: on mac you need to reach in the .app folder
options.setChromeBinaryPath("Chromium.app/Contents/MacOS/Chromium");

var driver = new chrome.Driver(options, service);
Matthew Sanders
  • 4,875
  • 26
  • 45