What to set app_host to for Capybara - ruby

My tests attempt to visit webpages and verify that certain elements exist on the pages. For example, it visits http://foo.com/homepage.html and checks for a logo image, then visits http://bar.com/store/blah.html and checks that certain text appears on the page. My goal is to visit Kerberos authenticated webpages.
I found Kerberos code as below:
Main file
uri = URI.parse(Capybara.app_host)
kerberos = Kerberos.new(uri.host)
#kerberos_token = kerberos.encoded_token
kerberos.rb file
class Kerberos
def initialize(host)
#host = host
#credentials = AuthGss::Negotiate.new("HTTP##{#host}")
#credentials.cache = ENV['KRB5CCNAME'] if ENV['KRB5CCNAME']
#token = #credentials.step("")
end
def encoded_token
Base64.encode64(#token).gsub(/\n/,"")
end
end
It utilizes Capybara.app_host value. I can't figure out what to set the Capybara.app_host value to. I can't figure out what it does. I have Capybara.run_server = false. Can someone help me understand how to use Capybara.app_host and how this relates to Kerberos authentication?

The Capybara docs show an example of using a remote host. app_host is the base host of your web application:
Capybara.current_driver = :selenium
Capybara.run_server = false
Capybara.app_host = 'http://www.google.com'
visit('/users') # goes to http://www.google.com/users

I was confused about app_host, but after a dig around in the Capybara source code it seems like it doesn't actually do very much. In fact, it only seems to be used at one place in the Capybara source code:
if url_relative && Capybara.app_host
url = Capybara.app_host + url
url_relative = false
end
Basically, if you pass a relative URL like /posts/1/edit to a method like visit, Capybara will visit the URL "#{Capybara.app_host}/posts/1/edit`. So the following two snippets of code are equivalent:
# 1:
Capybara.app_host = "https://stackoverflow.com"
visit "/questions/19991349"
# 2:
visit "https://stackoverflow.com/questions/19991349"
By default RSpec sets app_host to http://localhost, which is why you can write code like before { visit edit_post_path(1) } when using Capybara and Rails straight out of the box, and you don't have to write localhost every time.

Related

Capybara instance of "browser" for Page Object Model?

Im writing a framework using Capybara and the Page Object Model for a web application. It's my first time writing my own framework and using PoM for automation.
My base "Page Object" essentially initializes the driver and is used in every other page object child class (for the individual pages)
class PageObject
include Capybara::DSL
BASE_URL = 'https://www.atesturl.com'
Capybara.default_max_wait_time = 5
def initialize
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
opts.args << '--disable-site-isolation-trials'
end
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
Capybara.register_driver :selenium_chrome_headless do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
opts.args << '--headless'
opts.args << 'window-size=2880,1800'
opts.args << '--disable-gpu' if Gem.win_platform?
#opts.args << '--remote-debugging-port=9222'
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
opts.args << '--disable-site-isolation-trials'
end
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
Capybara.current_driver = :selenium_chrome
end
def visit_url
visit BASE_URL
end
end
In most examples of PoM I see the methods returning an instance of that page object, but generally they use some #browser instance variable that is passed around. Within my test scripts I simple call an instance of the Base page object class via let(:p) {PageObject.new} and then p.visit_url then make new instances of the other page objects via new....but this seems like the wrong way to do it.
How exactly do I return a instance of the #browser or driver that I can pass around? And how should I be calling it in my spec?
You don't want to be registering your drivers in the initialize for the base PageObject since that means every object created will be registering new driver configs - which isn't desirable.
When you include Capybara::DSL into your class you're including methods that end up calling methods on Capybara.current_session. ie visit => Capybara.current_session.visit. The result of Capybara.current_session is the "#browser" instance you're asking about since it encapsulates the driver/browser instance. The issue with the way you've currently implemented that is that if any code changes the current session then all your objects will suddenly refer to the new session. If instead you store a reference to the session you want the object to use in each object and call the Capybara methods on that session rather than using Capybara::DSL (#session.visit ...) then you can be sure the session an object is using doesn't change unexpectedly.
Also note that things like Capybara.default_max_wait_time, `Capybara.current_driver', etc are global settings so setting them inside your PageObject class isn't a great idea.

What is does this error method mean: undefined method `method name' for nil:NilClass (NoMethodError)

I am fairly new to automation testing and I am writing BDD automation test scenarios in Ruby using selenium-webdriver, when running my tests, they fail at the first step. (tumblr just as an example)
What does this error message mean and how do I fix it? Any help would be much appreciated!
In my feature file:
Feature: tumblr
#s1
Scenario: Logging in to Tumblr
Given I am on the Tumblr login page
When I enter my login details
Then I should be sent to the dashboard
In my login_page.rb:
def visit
#browser.goto "#{EnvConfig.base_url}/login"
await_on_page
end
In my login_step_defs.rb:
Given /^I am on the Odicci login page$/ do
#app.tumblr_login.visit
end
When /^I enter my login details$/ do
#app.tumblr_login.login
end
Then /^I should be sent to the dashboard$/ do
#app.tumblr_dashboard.go_to_dashboard
end
Initially when I was running 'cucumber features.feature' but the step definitions could not be located so the scenarios were finishing off as 'undefined' so running 'cucumber features.feature -r step_definitions works to run the tests but they fail because of this error message:
Scenario: Logging in to Tumblr # features.feature:4
Given I am on the Tumblr login page # step_definitions/login_step_defs.rb:2
undefined method `tumblr_login' for nil:NilClass (NoMethodError)
./step_definitions/login_step_defs.rb:3:in `/^I am on the Tumblr login page$/'
features.feature:5:in `Given I am on the Tumblr login page'
#maxpleaner
if ENV['HEADLESS']
require 'headless'
require 'selenium-webdriver'
headless = Headless.new display: '100'
headless.start
end
# Set up browser
# browser = Watir::Browser.new (ENV['BROWSER'] || 'chrome').to_sym
driver = Selenium::WebDriver.for :chrome
browser_type = ENV['BROWSER'] || 'chrome'
$setup_done = false
Before do |scenario|
#browser = browser
#app = App.new #browser
unless $setup_done
$setup_done = true
# This stuff will only run before the first scenario executed. Use it to set up data etc.
end
end
After do |scenario|
end
at_exit do
browser.quit
end

How to properly debug with Capybara/Poltergeist?

I am playing around with capybara/poltergeist perfect duo, but I am having trouble to properly debugging.
I was testing a simple script:
logger = Logger.new "./log/who-scored-com.log"
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false,
debug: true,
logger: logger)
end
browser = Capybara.current_session
browser.visit 'https://www.whoscored.com/LiveScores'
browser.save_page 'page.html'
I am expecting that the script grap the page normally and saves it, but the page is empty and this is returned:
`Capybara::Poltergeist::StatusFailError: Capybara::Poltergeist::StatusFailError
from /home/vagrant/local/ruby-2.3.0/lib/ruby/gems/2.3.0/gems/poltergeist-1.9.0/lib/capybara/poltergeist/browser.rb:351:in `command'
from /home/vagrant/local/ruby-2.3.0/lib/ruby/gems/2.3.0/gems/poltergeist-1.9.0/lib/capybara/poltergeist/browser.rb:34:in `visit'`
Now, this don't give me nothing about this error. I catch the exception and print it and it gives me:
"Request to 'https://www.whoscored.com/LiveScores' failed to reach server, check DNS and/or server status"
Even if I have no idea why the address do not respond for capybara (and any hint would be appreciate :) ) I don't understand why the :debug options used in configuration doesn't seem to give me no info
You have a couple of issues
the poltergeist logger option is defined as '(Object responding to puts) - The ruby 2.3.0 standard library Logger object doesnt respond to puts so it's not valid.
You're example doesn't have Capybara.current_driver = :poltergeist so I'm not sure if it is actually using the driver you're configuring there or a previously defined one (I would have expected an error on the Logger object if it was)
debug: true will add debugging for poltergeist to the log, but there is also debugging info from phantomjs. That is generated by passing phantomjs_options: ['--debug=true'], phantomjs_logger: <an IO object - again not a Logger object> to the driver
The error you're actually hitting is the connection being refused due to not being able to negotiate an ssl protocol - to fix it add the required ssl protocol as a phantomjs option - `phantomjs_options: ['--ssl-protocol=TLSv1.2']
I'd use something like the following in a standalone ruby script, adjust driver_options to suit your taste.
require 'capybara'
require 'capybara/poltergeist'
class NilLogger
def puts * ; end
end
def setup_session
driver_options = { js_errors: false,
logger: NilLogger.new,
phantomjs_logger: STDOUT,
phantomjs_options: ['--debug=true'],
debug: false }
Capybara.configure do |conf|
conf.run_server = false
conf.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new app, driver_options
end
conf.current_driver = :poltergeist
end
Capybara.current_session
end
browser = setup_session()
browser.visit 'https://www.whoscored.com/LiveScores'
browser.save_page 'page.html'
Have you tried
Capybara.register_driver :poltergeist_debug do |app|
Capybara::Poltergeist::Driver.new(app, :inspector => true)
end
Capybara.javascript_driver = :poltergeist_debug
as shown here?

Why can't PhantomJS/Poltergeist pull up this website correctly?

I'm using Capybara to navigate a web page. The url look like this:
http://www.myapp.com/page/ABCXYZ?page=<x>
The page has a paginated table on it. Passing a page number to it will paginate the table appropriately.
However, when using the poltergeist driver, the page parameter is always ignored.
Using the selenium driver is not an option because it's a hassle to get it to run headless, it doesn't want to run more than one time (gives "connection refused" error on localhost).
This looks like an encoding issue, but I'm not sure where exactly in the stack that the issue lies.
class Verifier
class << self
include Capybara::DSL
Capybara.default_driver = :poltergeist
Capybara.default_wait_time = 10
def parse_table(header)
xpath = '//*[#id="products"]/table[3]/tbody/tr/td/div[4]/div/table'
table = find(:xpath, xpath)
rows = []
table.all("tr").each do |row|
product_hash = {}
row.all("td").each_with_index do |col,idx|
product_hash[header[idx]] = col.text
end
rows << product_hash
end
rows
end
def pages
page.find(".numberofresults").text.gsub(" Products","").split(" ").last.to_i/25
end
def import(item)
visit "http://www.myapp.com/page/#{item}"
header = parse_header
apps = parse_vehicles(header)
pages.times do |pagenumber|
url = "http://www.myapp.com/page/#{item}?page=#{pagenumber+1}" # This is the problem
end
end
end
That url in the last loop? It is processes as if the pagenumber is not present. When I change the driver to :selenium, this whole thing works. So it's not a Capybara issue as far as I can see.

Mechanize (Ruby gem) not recognizing a form from a saved HTML file, but it recognizes the form when accessing the actual website?

I'm trying to write Rspec tests for my Mechanize agent.
My agent is supposed to go to a website, log into the form, then scrape some data off the website. I also downloaded FakeWeb to stub the HTTP requests, and make my tests faster.
Here is my account_spec.spec file:
require 'spec_helper'
describe Account do
before(:each) { #account = Account.new('bob', '1234') }
describe '#login' do
before(:each) do
home_page = File.read('spec/html/home_page.html')
login_page = File.read('spec/html/login_page.html')
FakeWeb.register_uri(:get,
"https://www.example.com/",
body: home_page,
status: ["200", "Success"],
content_type: "text/html")
FakeWeb.register_uri(:get,
"https://www.example.com/account/login",
body: login_page,
status: ["200", "Success"],
content_type: "text/html")
#web_crawler = Mechanize.new
#home_page = #web_crawler.get("https://www.example.com/")
#login_page = #web_crawler.get("https://www.example.com/account/login")
end # -- before :each
it 'finds the login form' do
login_form = #login_page.form_with(:class => "form login")
puts login_form.class # ==> nil:NilClass
end
end # -- #login
end # -- Account
However, when I comment out the FakeWeb uri for example/account/login (it then accesses the real server), it actually returns the correct form. Basically, if I am searching for the form in my locally saved HTML file, Mechanize can not find it, but if I check the actual website, it does find it. I would like to know if there is a way around this, and why this happens.
Any help would be greatly appreciated.

Resources