0

I'm using Capybara::Selenium from my script (not tests) for getting some image from external site. Page loads fine, all images are loaded also and I see them, but any attempt to execute function page.session.driver.evaluate_script always throws Net::ReadTimeout: Net::ReadTimeout with #<TCPSocket:(closed)>.

Full code:

require 'capybara-webkit'
require 'selenium-webdriver'

JS_GET_IMAGE = <<~EJSGETIMAGE
  var img = document.getElementById('requestImage');
  const cvs = document.createElement('canvas');
  cvs.width = img.width;
  cvs.height = img.height;
  cvs.getContext('2d').drawImage( img, 0, 0 );
return cvs.toDataURL("image/png");
EJSGETIMAGE

session = Capybara::Session.new :selenium
page = session.visit Cfg.site.url
driver = session.driver.browser
driver.manage.timeouts.script_timeout = 5000

@img = driver.execute_async_script JS_GET_IMAGE

Okay, I started to test very simple script, but that also gone to the same error.

page.session.driver.browser.execute_async_script("setTimeout(arguments[0], 2000)")

Also I used session = Capybara::Session.new :selenium_headless and got the same error.

  • ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
  • selenium-webdriver (3.142.3)
  • capybara (3.28.0)
  • capybara-webkit (1.15.1)
  • community/geckodriver 0.24.0-1
  • firefox 68.0.1 (64-битный)

Any help is very appreciated.

1 Answer 1

2

Small thing first - there is no need to load capybara-webkit if you're using the Selenium driver.

Now onto the main issue. There is no need to call methods directly on driver when executing JS, rather you should just be calling the Capybara methods execute_script, evaluate_script, or evaluate_async_script. The evaluate_xxx methods are for when you expect a return value, the execute_script method is for when you don't care about any return value. evaluate_async_script receives a callback function as the last argument which needs to be called to return a value, but your JS_GET_IMAGE doesn't appear to ever do that (nor really need to since it's not async) so it would be better to just use evaluate_script. The other requirement for evaluate_script is that the code evaluated needs to be a single statement. To meet that requirement we can use an IIFE.

require "capybara/dsl"

JS_GET_IMAGE = <<~EJSGETIMAGE
  (function(){
     var img = document.getElementById('requestImage');
     const cvs = document.createElement('canvas');
     cvs.width = img.width;
     cvs.height = img.height;
     cvs.getContext('2d').drawImage( img, 0, 0 );
     return cvs.toDataURL("image/png");
  })()
EJSGETIMAGE

session = Capybara::Session.new :selenium
session.visit Cfg.site.url
@img = session.evaluate_script JS_GET_IMAGE

although IMHO it would be better to have Capybara find the element and pass it to the JS function making it more flexible and taking advantage of Capybaras waiting for elements to appear

require "capybara/dsl"

JS_GET_IMAGE = <<~EJSGETIMAGE
  (function(img){
     const cvs = document.createElement('canvas');
     cvs.width = img.width;
     cvs.height = img.height;
     cvs.getContext('2d').drawImage( img, 0, 0 );
     return cvs.toDataURL("image/png");
  })(arguments[0])
EJSGETIMAGE

session = Capybara::Session.new :selenium
session.visit Cfg.site.url
img_element = session.find('#requestImage')
@img = session.evaluate_script JS_GET_IMAGE, img_element
Sign up to request clarification or add additional context in comments.

2 Comments

Your first js example gives me Selenium::WebDriver::Error::JavascriptError: TypeError: img is null but the last one is ok. Thank you!
@Dimitri Yes - if you executed the script before the id=requestImage element was on the page - then var img = document.getElementById('requestImage'); would set img to null - which means when trying to do img.width you'd get that error. That's one of the reasons why it's better to use Capybara to find the element and pass it in to the JS, because Capybara will wait a bit for the element to be on the page if it isn't yet there initially.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.