Headless operations don't work inside Sinatra route - ruby

I am using the headless and selenium-webdriver gems to launch a headless Firefox browser:
headless = Headless.new(
video: {
frame_rate: 12,
codec: 'libx264'
}
)
headless.start
driver = Selenium::WebDriver.for(:firefox)
With this code I can write the following:
driver.navigate.to("http://google.com")
Yet the following raises an error after I visit '/' in the browser:
get '/' do
driver.navigate.to("http://google.com")
erb :root
end
The error is as follows:
*** Errno::ECONNREFUSED Exception: Failed to open TCP connection to 127.0.0.1:7055 (Connection refused - connect(2) for "127.0.0.1" port 7055)
I'm pretty sure this is because the driver.navigate is not being called in the headless scope, therefore it can't connect to the Firefox instance.
I have also tried using the modular sinatra style, but the same error occurred.
workaround
What I ended up doing is separating the headless server in a separate script. This script has loop and gets input, printing the output of running the command in the headless scope. Then from the sinatra server, i use PTY.spawn to instantiate the server and pass around its stdin and stdout so I can use it in my Sinatra app. This way the headless script is only run once (therefore multiple headless servers aren't started) and I can connect to it from my Sinatra routes. I can't interact with the headless script's variables or methods directly - I need to just use i/o.
I am hoping for an answer which hows how to make the original code work, though (when the sinatra app's routes are called in headless scope)

One possible trick that can help:
this = self
get '/' do
this.driver.navigate.to("http://google.com")
erb :root
end

I ended up getting this working.
At first I thought that what fixed it was doing the headless environment initialization in the scope of a sinatra route, i.e.
get '/' { do_initialization_here }
The real fix may have been in the way I was calling Headless.new (I originally had a bunch of options tacked on and I removed all them).

Related

Prevent phantomjs from raising Capybara::Poltergeist::StatusFailError when requesting never ending assets

I am having some issues with Capybara::Poltergeist::Driver
When I visit the the following url with poltergeist, I am exerpiencing an issue where an asset that seemingly doesn't exist takes for ever to load and eventually an error gets raised: https://www.feinstein.senate.gov/public/index.cfm/e-mail-me
$ brew install phantomjs
$ gem install capybara -v 2.17.0
$ gem install poltergeist -v 1.7.0
$ gem install selenium-webdriver -v 2.53.4
Then in irb:
require 'capybara/poltergeist'
module Drivers
class Poltergeist < Capybara::Poltergeist::Driver
def needs_server?
false
end
end
end
Capybara.register_driver :poltergeist_errorless do |app|
options = ['--load-images=no', '--ignore-ssl-errors=yes', '--ssl-protocol=any', '--disk-cache=true', '--max-disk-cache-size=500000']
Drivers::Poltergeist.new(app, js_errors: false, phantomjs_options: options)
end
session = Capybara::Session.new(:poltergeist_errorless)
session.visit('https://www.feinstein.senate.gov/public/index.cfm/e-mail-me')
After 10-20 seconds, the request fails, and I get back a Capybara::Poltergeist::StatusFailError exception with a message that says:
Request to 'https://www.feinstein.senate.gov/public/index.cfm/e-mail-me' failed to reach server, check DNS and/or server status - Timed out with the following resources still waiting https://sdc1.senate.gov/NEED_VALUE/wtid.js
But if I then call:
session.save_screenshot('/tmp/sc.png', full: true)
the outputted screenshot is shows that the rest of the page loaded just fine. If this were any other browser, it would just continue to function happily without worrying about an asset that is taking forever to load.
Is there anyway to configure phantomjs to not wait for this asset and to not raise this exception?
The easiest way to deal with that is to use Poltergeists blacklist to block the url - https://github.com/teampoltergeist/poltergeist#customization -
and/or - https://github.com/teampoltergeist/poltergeist#url-blacklisting--whitelisting
If your situation is more dynamic you could rescue the exception, parse out the URL, add it to the blacklist, and then retry the visit.
Additionally, there is no need to override needs_server?. If you don't pass a second parameter (the app to run) to Session#new (which you aren't doing) then needs_server? is irrelevant.
I'll play around with the session timeout params:
session = Capybara::Session.new(:poltergeist_errorless, :timeout=>ASSET_LOAD_TIME)

How to write a simple Cucumber script

I'm following the tutorial to run my first Cucumber script:
Feature: guru99 Demopage Login
In order to Login in Demopage we have to enter login details
Scenario:
Register On Guru99 Demopage without email
Given I am on the Guru99 homepage
When enter blank details for register
Then error email shown
I have the project in Idea but when I run it I get errors.
When using chrome:
Failed to open TCP connection to 127.0.0.1:9515 (No connection could be made because the target machine actively refused it.
I have no idea how to resolve it.
When using Firefox, the script successfully opens the browser but fails after that:
require 'watir'
require 'colorize'
Selenium::WebDriver::Firefox::Binary.path='C:\soft\Mozilla Firefox\firefox.exe'
case ENV['BROWSER']
when 'chrome'
browser = Watir::Browser.new :chrome
when 'firefox'
browser = Watir::Browser.new :firefox
end
Given(/^I am on the Guru99 homepage$/)do
#browser = Watir::Browser.new :firefox
#browser.goto "http://demo.guru99.com"
end
When(/^enter blank details for register$/) do
browser.text_filed(:name,"emaiid").set("")
browser.button(:name,"btnLogin").click
end
Then(/^error email shown$/) do
puts "Email is Required!".red
browser.close
end
And returns:
NoMethodError: undefined method `text_filed' for nil:NilClass
on this line:
browser.text_filed(:name,"emaiid").set("")
I found some references that I need to write a class to call a method. I tried it but didn't succeed.
Connection refused, I'm unsure but "Watir+Cucumber Connection refused" looks a fix.
Copy pasta:
AfterConfiguration do |config|
yourCodeStartUp() # Put your SETUP code here including the launch of webdriver
at_exit
yourCodeTearDown() # Put your CLOSING routine here
puts 'stopped'
end
end
The code error is a typo, it should be browser.text_field(...
Regarding the issue you are observing on chrome, it sounds like you need to update chromedriver (and make sure the exe is in PATH). If you are running chrome v56-58, you need ChromeDriver 2.29.
Regarding the NoMethodError: undefined method error, you have a typo when you call the text_field method (i.e. browser.text_filed).
Nope, I was mistaken, I had an older version of chromedriver. Now it's running in chrome as well. Thank you very much. Appreciate your time!
So, the answers are:
1. Update chromedriver.
2. Check your code for typos one more time.
Was really easy but took me a lot of time%

Testing server ruby-application with cucumber

My ruby application runs Webrick server. I want to test it by cucumber and want to ensure that it gives me right response.
Is it normal to run server in test environment for testing? Where in my code I should start server process and where I should destroy it?
Now I start server by background step and destroy in After hook. It's slow because server starts before every scenario and destroys after.
I have idea to start server in env.rb and destroy it in at_exit block declared also in env.rb. What do you think about it?
Do you know any patterns for that problem?
I use Spork for this. It starts up one or more servers, and has the ability to reload these when needed. This way, each time you run your tests you're not incurring the overhead of firing up Rails.
https://github.com/sporkrb/spork
Check out this RailsCast for the details: http://railscasts.com/episodes/285-spork
Since cucumber does not support spork any more (why ?) I use the following code in env.rb
To fork a process I use this lib : https://github.com/jarib/childprocess
require 'childprocess'
ChildProcess.posix_spawn = true
wkDir=File.dirname(__FILE__)
server_dir = File.join(wkDir, '../../site/dev/bin')
#Because I use rvm , I have to run the server thru a shell
#server = ChildProcess.build("sh","-c","ruby pageServer.rb -p 4563")
#server.cwd = server_dir
#server.io.inherit!
#server.leader = true
#server.start
at_exit do
puts "----------------at exit--------------"
puts "Killing process " + #server.pid.to_s
#server.stop
if #server.alive?
puts "Server is still alive - kill it manually"
end
end

How to designate a Rack handler

Rackup is successfully running any Rack app via Rack's default handler. e.g.:
class RackApp
def call(environment)
[
'200',
{'Content-Type' => 'text/html'},
["Hello world"]
]
end
end
run RackApp.new
But rackup is giving "NoMethodError at / undefined method `call' for nil:NilClass" when the last line is changed to instead use Rack's built-in CGI handler:
Rack::Handler::CGI.run RackApp.new
The same objection is raised for Rack's other built-in handlers. e.g. Rack::Handler::Thin, Rack::Handler::FastCGI, even Rack::Handler::WEBrick (which is the handler Rack selects above in default mode).
What's the correct syntax here?
The rackup command reads the config file and starts a server. The Rack::Handler::XXX.run methods also start a server, independently of the rackup command (CGI is slightly different as it isn't actually a server as such).
What happens when you change the line
run RackApp.new
to
Rack::Handler::CGI.run RackApp.new
and run rackup is as follows. The server starts and parses the config file. When the Rack::Handler::CGI.run RackApp.new line is reached it is executed as any other Ruby code would be. In the case of the CGI handler this calls the app and writes the output to the standard output as it would if running as a CGI script (have a look at your terminal when you run rackup). Afterwards the 'rackup' server is started as normal, but without an app to run. When you try to access the page you'll get the NoMethodError, since the app is nil.
Using Rack::Handler::Thin is similar, but in this case, as Thin actually is a web server, it is started and will serve RackApp, but listens on Thin's default port of 8080 (not the rack default of 9292). After stopping Thin (e.g. with Ctrl-C) the default rackup server (Mongrel or Webrick) will start listening on port 9292, again with no app specified so you'll get the NoMethodError.
If you run your modified 'config.ru' as a plain Ruby script rather than using rackup you'll see the same behaviour, but without the rackup server being started. (You'll need to require rack first, so use ruby -rrack config.ru). In the CGI case the output of a single call to your app will be printed to the console, in the Thin case Thin will be started serving your app.
In order to specify the server to use with rackup, you can use the -s option, e.g. rackup -s thin will start the app using Thin (this time on the rackup default port of 9292). You can also do rackup -s cgi but this won't really work in any useful way - it just prints out the html of an error page to the console.
CGI
If you're trying to run your app as a CGI there are a couple of options. You need to create a CGI script that calls your app using the CGI handler. This could itself be a ruby script that calls Rack::Handler::CGI.run directly, in fact you could use your modified config.ru directly (you might want to rename it first and add an explicit require 'rack' line).
Alternatively you can use a shell script which then calls rackup config.ru. In this situation rackup detects that it's running as CGI and automatically uses the correct handler
http://guides.rubyonrails.org/rails_on_rack.html (see point 3.5)
also interesting:
http://railscasts.com/episodes/151-rack-middleware
http://railscasts.com/episodes/222-rack-in-rails-3

Open Firefox browser with Ruby automation script

How is it possible to open FireFox browser by Ruby(for automation script)?
I use
#browser = RSpecSeleniumHelper.connect_browser('/admin/', '*firefox')
but it doesn't work.
You can start any program in ruby with:
`firefox http://www.google.com`
or
system("firefox http://www.google.com")
You can use Watir, as it supports Firefox also:
http://wtr.rubyforge.org/platforms.html
You may have to check if the Selenium Remote Control is start or not, normally it is running at port 4444.
java -jar selenium-server-xxx.jar
then you can use
#browser = Selenium::Client::Driver.new(
:host => "localhost",
:port => 4444,
:browser => "*firefox", #*iexplore, *firefox3, *safari...
:url => "http://www.google.com/",
:timeout_in_second => 60)
#browser.start_new_browser_session
Hope this helps, you can find more demo by download Selenium RC
I encountered two issues while getting this running:
If you are running your Ruby app from MacOS, the command firefox may not be properly aliased by default and so may fail without errors printed to your Ruby console.
If you already have an instance of Firefox open, you will get a message saying "Close Firefox - A copy of Firefox is already open. Only one copy of Firefox can be open at a time."
This code fixes both problems:
system("open -a /Applications/Firefox.app/Contents/MacOS/firefox-bin http://www.google.com http://www.cpap.com")
open's -a option Opens with the specified application.
The file path list works for me. If it won't load for you, first drop it and try plain "firefox" and failing that try "/Applications/Firefox.app/Contents/MacOS/firefox"
The example above shows two URLs separated by a space. You can use just one URL or as many as you care to following this pattern.

Resources