Reputation: 633
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.
Any help is very appreciated.
Upvotes: 0
Views: 413
Reputation: 49870
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
Upvotes: 2