ruby selenium execute_script Net::ReadTimeout - ruby

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.

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

Related

How can we use existing selenium scripts (java) to get some end to end performance metrics (low load)

I am trying to leverage existing selenium automation scripts in java developed by Automation team to get some end to end performance metrics (total page load time etc) - very minimal load, in parallel to api load testing.
Please let me know what will be the most efficient way to do that. Also i am looking for making minimum changes to the selenium scripts because this will be an ongoing activity with each release and i am looking for to use functional scripts as it is with performance wrapper around it.
Your suggestions will be appreciated.
thank you!
I would avoid using Selenium for load testing. If setting up dedicated perf tests is not an option, you could at least do this:
//Put this into the loaded scripts of your page under test. Make sure loadTimeMs is a global variable or saved to a hidden control.
var loadTimeMs = 0;
var pageContentRecieved = (new Date()).getTime();
$(window).load(function () {
var pageLoaded = (new Date()).getTime();
loadTimeMs = pageLoaded - pageContentRecieved;
});
That will give you an approximate time to load the sync parts of a page. More importantly is the time for the web server to provide you the content. I recommend doing that in a controlled way by calling the page back as an API in the javascript.
//Return previous value for page load time.
JavascriptExecutor je = (JavascriptExecutor)driver;
int pageLoad = je.executeAsyncScript("return loadTimeMs;");
//Goes into Java code.
je = (JavascriptExecutor)driver;
int apiResponseTime = je.executeAsyncScript(
"var loadTimeMs = 0;" +
"var pageContentRecieved = (new Date()).getTime();" +
"$.ajax({url: "demo_test.txt", success: function(result){" +
"var pageLoaded = (new Date()).getTime();" +
"return pageLoaded - pageContentRecieved;" +
"}});" +
);
int fullLoadTime = pageLoad + apiResponseTime;
Finally, add the two values together for a rough/approximate performance check in the middle of your existing selenium tests.

Explicit wait for Selenium Webdriver

Working on the method trying to understand the explicit wait.
require 'rubygems'
require 'selenium-webdriver'
require 'cucumber'
$driver = Selenium::WebDriver.for :firefox
$driver.manage.timeouts.implicit_wait = 3
Then /^do search$/ do
driver = $driver
one_way = driver.find_element(:id, "search.ar.type.code.oneWay").click
sleep 5
from = driver.find_element :xpath => "//div[#class = 'origin column1']//input[#type = 'text']"
from.click
So after one_way radio button is clicked and input form changed , so I put sleep 5 to give it a time element to appear, otherwise would be error "element not visible ...". So I thought it would be good time to understand explicit wait, because I need to wait till element will appear.
wait = Selenium::WebDriver::Wait.new(:timeout => 40)
wait.until {from = driver.find_element(:xpath, "//div[#class = 'origin column1']//input[#type = 'text']")
from.click
}
But getting error "Selenium::WebDriver::Error::ElementNotVisibleError: Element is not currently visible and so may not be interacted with" . Why this code doesn't wait till element appears and click it?
The issue is that the element isn't in the DOM yet, hence your needing to put a time delay in there.
That said, the API doco for ruby says you should do it this way
require 'rubygems' # not required for ruby 1.9 or if you installed without gem
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://somedomain/url_that_delays_loading"
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
begin
element = wait.until { driver.find_element(:id => "some-dynamic-element") }
ensure
driver.quit
end
note that the element that you can then .click() is assigned from the wait.until method not the find_element() method as in your code.
However that arbitrary delays don't always work, if the site is busy then the delay may not be long enough.
A better option is to wait for the element to become clickable or visible.
The Java API's have ExpectedConditions convenience methods that cn be used like this...
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
element.click();
Unfortunatly I don't think Ruby has this yet. You may need to code up your own ExpectedCondition class or package.
I'm not a Ruby dev but here is the idea of a function in Java that could be used to implement your ExpectedCondition
public WebElement findElementInTime(WebDriver driver, By by, int timeoutInSeconds)
{
log.debug("==================+>>>>>> looking for element for "+ timeoutInSeconds + " seconds");
WebElement ret = null;
for (int second = 0; second < timeoutInSeconds; second++) {
try { if (isElementPresent(driver,by)) {
log.debug("found element :-)");
ret = driver.findElement(by);
break;
}} catch (Exception e) {
log.debug("oops... sleeping 1 sec wait for button");
}
log.debug("sleeping 1 sec wait for button");
}
return ret;
}

Display right image according to screen size

It's maybe an easy question but after some research I didn't find any valid solution.
I use carrierwave to generate multiple versions of an uploaded image. Each image has a specific size for different screen sizes.
For example (in my uploader) I have
version :xl
for screens larger than 1200px.
How can I use rails to display specific image according to client screen size ?
I know there is gem responsive-images but I can't use size.
The subject of responsive images is for the moment quite vast and nobody seems to have the same answer, especially with javascript and html5. How about rails (even if it's server side) ?
Here are a couple of ways:
Load the page with all the paths to various image sizes stored as data attributes, grab the screen width with js, then stick an image tag with the correct path where you want it. Code might look like (not tested, and I've been using coffeescript, so probably missing details):
# ruby command line
$ #image.mobile_asset_url
=> "path/to/mobile/image"
$ #image.desktop_asset_url
=> "path/to/desktop/image"
# html.erb
<img src="" class="placeholder" data-mobile-url=<%=#image.mobile_asset_url%> data-desktop-url=<%=#image.desktop_asset_url%>></div>
# javascript
el = $('.placeholder')
data = el.data();
width = $(window).width();
mobile_breakpoint = 480;
if (width <= mobile_breakpoint) {
url = data.mobileUrl;
} else {
url = data.desktopUrl;
}
el.attr('src', url)
Do a server side test not for screen width, but browser type with a gem like browser, and load smaller images for mobile specific browsers like iOS8. Logic like this might go best in a decorator. I'm more fluent with Ruby, so an example might look like (this code not tested):
# user_decorator.rb, from the Draper gem
class UserDecorator
def profile_url
if mobile_browser?
"path/to/small/image"
else
"path/to/large/image"
end
end
private
def mobile_browser?
browser.ios || browser.android || ...
end
end
Well, I really wanted to do it in a more rails way. I ended up using ajax :
Fire ajax at load page :
# assets/javascript/cards/index.js
// fire loading image
$.ajax({
url: document.URL,
data: {
w: window.innerWidth
}
});
In controller, respond to ajax :
# controllers/cards_controller.rb
render 'cards/images/loadimage' if request.xhr?
Then load your images
# cards/images/loadimage.js.erb
<% w = params[:w].to_i %>
document.getElementById('banner-container').innerHTML = "<%= j image_tag #card.banner_type_url(w), class: 'banner' %>";
document.getElementById('logo-container').innerHTML = "<%= j image_tag #card.logo_type(w), class: 'logo' %>";
I made in my model a method to return right image given screen width (and based on carrierwave version) :
# models/card.rb
# returns banner type based on width (w)
def banner_type_url(w)
return self.banner.s.url || banner_default('s') if(w <= 768)
return self.banner.m.url || banner_default('m') if(w <= 992)
return self.banner.l.url || banner_default('l') if(w <= 1200)
return self.banner.xl.url || banner_default('xl') if(w <= 1920)
return self.banner.full.url || banner_default('full')
end
def banner_default(s)
"card/banner/#{s}_default-banner.jpg"
end

Firefox Addon API for Taking Screenshot

I am looking for firefox addon api to take screenshot of visible area of document.
Chrome and Safari have api's to achieve this. And they are pretty fast.
I could not find anything specific for firefox.
I found a workaround at How do I use the canvas drawWindow function in an addon created using the addon sdk? but this solution takes full page screenshot with scrolls including (hidden parts of document). There are 2 issues for this solution;
1- if page has long scroll, it takes long time to complete screenshot process. Because it is using canvas based drawing.
2- I would like to get screenshot of visible area of document, not whole document.
Is there any workaround for this?
Thanks.
Using the SDK you can do something like this:
const { window: { document } } = require('sdk/addon/window');
const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
document.documentElement.appendChild(canvas);
function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
let contentWindow = getTabContentWindow(tab);
let w = contentWindow.innerWidth;
let h = contentWindow.innerHeight;
let x = contentWindow.scrollX;
let y = contentWindow.scrollY;
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext('2d');
ctx.drawWindow(contentWindow, x, y, w, h, '#000');
return canvas.toDataURL();
}
That should takes only the visible area. By default, it grabs the active tab, but you can pass any other tab – because is designed as low level API it takes a native tab, however, not a SDK tab.
You can put in a module and exports just the captureTab function.
Edit: e10s version
The code above is not currently compatible with Firefox with e10s available, as Ian Bicking noted in the comment. An easy way to workaround this issue, is create a temporary canvas in the same document and content process we want to capture the screenshot:
const { getTabContentWindow, getActiveTab } = require('sdk/tabs/utils');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
function captureTab(tab=getActiveTab(getMostRecentBrowserWindow())) {
let contentWindow = getTabContentWindow(tab);
let { document } = contentWindow;
let w = contentWindow.innerWidth;
let h = contentWindow.innerHeight;
let x = contentWindow.scrollX;
let y = contentWindow.scrollY;
let canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
canvas.width = w;
canvas.height = h;
let ctx = canvas.getContext('2d');
ctx.drawWindow(contentWindow, x, y, w, h, '#000');
let dataURL = canvas.toDataURL();
canvas = null;
return dataURL;
}
That works in both e10s and no-e10s FF version; the downside comparing to the previous one is creating a canvas every time we want to take a screenshot, but I think is acceptable.
Your assumption that taking a screenshot on Firefox with canvas is somehow slow is wrong.
I did a couple of screenshots and Firefox/canvas was faster than Chrome/captureVisibleTab.
Actually Firefox is better suited for as-fast-as-possible screenshots, since its canvas expose to privileged code the mozFetchAsStream method, allowing to bypass the actual bottleneck which is the base64 encoding of the image data.
Some numbers
Chrome: captureVisibleTab 200-205ms
Firefox: drawImage 20-25ms + toDataURL 125-130ms
The devtools screenshot command is a good example of how to capture just the visible part
In all fairness, to make a meaningful comparison one has to take into account whether Chrome's PNG encoder favors compression over speed. Still, this doesn't change the fact that Firefox's canvas is fine.
edit: OK, that base64 encoding remark is dumb, I don't know what I was thinking. Perhaps what I should write instead is that Firefox's canvas is not only fast but also versatile.

Refresh image in Tkinter window

I am building an application to continuously display an image fetched from an IP camera. I have figured out how to fetch the image, and how to also display the image using Tkinter. But I cannot get it to continuously refresh the image. Using Python 2.7+.
Here is the code I have so far.
import urllib2, base64
from PIL import Image,ImageTk
import StringIO
import Tkinter
URL = 'http://myurl.cgi'
USERNAME = 'myusername'
PASSWORD = 'mypassword'
def fetch_image(url,username,password):
# this code works fine
request = urllib2.Request(url)
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)
imgresp = result.read()
img = Image.open(StringIO.StringIO(imgresp))
return img
root = Tkinter.Tk()
img = fetch_image(URL,USERNAME,PASSWORD)
tkimg = ImageTk.PhotoImage(img)
Tkinter.Label(root,image=tkimg).pack()
root.mainloop()
How should I edit the code so that the fetch_image is called repeatedly and its output updated in the Tkinter window?
Note that I am not using any button-events to trigger the image refresh, rather it should be refreshed automatically, say, every 1 second.
Here is a solution that uses Tkinter's Tk.after function, which schedules future calls to functions. If you replace everything after your fetch_image definition with the snipped below, you'll get the behavior you described:
root = Tkinter.Tk()
label = Tkinter.Label(root)
label.pack()
img = None
tkimg = [None] # This, or something like it, is necessary because if you do not keep a reference to PhotoImage instances, they get garbage collected.
delay = 500 # in milliseconds
def loopCapture():
print "capturing"
# img = fetch_image(URL,USERNAME,PASSWORD)
img = Image.new('1', (100, 100), 0)
tkimg[0] = ImageTk.PhotoImage(img)
label.config(image=tkimg[0])
root.update_idletasks()
root.after(delay, loopCapture)
loopCapture()
root.mainloop()

Resources