Selenium does not detect page load (Ruby) - selenium

I am currently using
- firefox 13
- selenium-server-standalone-2.23.1.jar
- selenium-client (1.2.18)
- rspec 1.2.8
Selenium stops here even if page has fully loaded
08:49:43.888 INFO - Command request: waitForPageToLoad[300000, ] on session 2718493e6d4640eea76d6cb3ab1a6fc3
require 'rubygems'
require "selenium/client"
require "selenium/rspec/spec_helper"
describe "Google Search" do
attr_reader :selenium_driver
alias :page :selenium_driver
before(:all) do
#selenium_driver = Selenium::Client::Driver.new \
:host => "localhost",
:port => 4444,
:browser => "*firefox",
:url => "http://www.google.com",
:timeout_in_second => 10
end
before(:each) do
selenium_driver.start_new_browser_session
end
# The system capture need to happen BEFORE closing the Selenium session
append_after(:each) do
#selenium_driver.close_current_browser_session
end
it "can find Selenium" do
page.open "/"
page.title.should eql("Google")
page.type "q", "Selenium seleniumhq"
page.click "btnG", :wait_for => :page
page.value("q").should eql("Selenium seleniumhq")
page.text?("seleniumhq.org").should be_true
page.title.should eql("Selenium seleniumhq - Google Search")
page.text?("seleniumhq.org").should be_true
page.element?("link=Cached").should be_true
end
end

I wonder if the google page is using AJAX which is confusing the page load event.
When I went to http://google.com I noticed that submitting the search only added #q=selenium to the url.
This could likely be confusing selenium. Maybe instead of wait for pageload you can wait for page element
Some search element xpath (for example) /html/body/div[1]/div[3]/div[3]/div[6]/div[2]/div[3]/div/div[2]/div[2]/div/div/ol/div[2]/li[1]/div (ugly I know)

Related

Poltergeist/PhantomJS not honoring client-side redirect

Most of my automated integration test suite relies on a third-party authentication system that uses a client-side redirect, over which I have no control:
if (this.SfdcApp && this.SfdcApp.projectOneNavigator) { SfdcApp.projectOneNavigator.handleRedirect('https://example.com/?authtoken=MYTOKEN&more_stuff=etc'); }
else if (window.location.replace){
window.location.replace('https://example.com/?authtoken=MYTOKEN&more_stuff=etc');
} else {;
window.location.href ='https://example.com/?authtoken=MYTOKEN&more_stuff=etc';
}
For some reason, Poltergeist/PhantomJS is stopping here and not honoring the redirect code. Is this because it's trying to use window.location.replace or window.location.href instead of window.location directly?
Here's my Poltergeist configuration in RSpec:
Capybara.register_driver :poltergeist_tls do |app|
poltergeist_options = {
js_errors: true,
phantomjs_options: ['--ssl-protocol=TLSv1.2']
}
Capybara::Poltergeist::Driver.new(app, poltergeist_options)
end
Here's a simplified version of my spec:
it 'logs the user in', js: true do
visit login_url
fill_in 'Email', with: email
fill_in 'Password', with: password
find('input[value="LOG IN"]').click()
expect(page).to have_selector '#logout-btn'
end
I'm using Capybara 2.7.1, Poltergeist 1.10.0, and PhantomJS 2.1.1.
Edit: I know that window.location was broken in PhantomJS once upon a time but that appears to have been fixed years ago.
However, when I add this to my spec:
#hackishly get the redirect URL
matches = page.body.match(%r{window.location.href ='(https?://.*)';})
redirect_url = matches[1]
evaluate_script("window.location = '#{redirect_url}'")
evaluate_script('console.log(window.location)')
...it's still outputting the old window.location instead of the newly set redirect_url. Huh??

Capybara Selenium Phantomjs Browser Initialization

I want to use capybara for headless browser but i want to use this driver: Selenium::WebDriver::Remote::Http::Default.new
How to use this driver for capybara? Need to know the browser initialization using that driver not poltergeist or webkit.
Here's the example for chrome initialization in capybara:
Capybara::Selenium::Driver.new(app, :browser => :chrome)
Selenium::WebDriver::Remote::Http::Default.new isn't a driver - it's an http_client that can be used by drivers - I think what you're asking for is to use an instance of Selenium::WebDriver::Remote::Bridge which can be done using
Capybara::Selenium::Driver.new(app, browser: :remote, ...)
where the ... includes other options like :http_client, :desired_capabilites, :url (url for the remote server that will control the actual browser)
The title of this questions mentions phantomjs but never mentions it in the actual question. If thats what you really want then it's
Capybara::Selenium::Driver.new(app, browser: :phantomjs, ...)
where there are similar options http_client, desired_capabilities, url, args, port
For Capybara, you can use Poltergeist driver on the top of Phantomjs. To use it you need to install it by gem install poltergeist or add this gem "poltergeist" to your Gemfile and run bundle install. Then add poltergeist option to your env.rb and change your Capybara.javascript_driver = :poltergeist. See the example below:
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist do |app|
options = {
:js_errors => false ,
# :timeout => 120,
# :debug => true,
# :inspector => true,
# :window_size => [1280, 1024],
# :logger => false,
# :inspector => false,
# :visible => false,
:js => true,
:timeout => 10000,
:phantomjs_options => %w[--load-images=no]
}
Capybara::Poltergeist::Driver.new(app, options)
end
Capybara.javascript_driver = :poltergeist

Rails integration test against page modification hack?

I'm using Capybara 1.1.2, Rails 3.1.3, rspec-rails 2.9.0, and Ruby 1.9.3p0.
Assume an app with standard and account_admin users. A standard user can create another standard user, but a standard user cannot create an account_admin user.
Of course the UI does not give the standard user the option of creating an account admin. But 30 seconds with Firebug and the user can re-write the HTML so it submits a POST request to create an account_admin.
How do I test that my app prevents this kind of simple hack?
The normal standard user test looks like this:
context "when standard user is signed in" do
before do
login_as standard_user
visit users_path # go to index
click_link('Add user') # click link like user would
end
describe "when fields are filled in" do
let(:new_email) { "new_user#example.com" }
before do
fill_in "Email", with: new_email
fill_in "Password", with: "password"
fill_in "Password confirmation", with: "password"
choose "Standard user" # radio button for Role
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
Is there a way to "fool" the test into taking a value not allowed on the form? I tried treating the radio button like a text field, but Capybara rejects it as a non-existent field:
fill_in "Role", with: "account_admin" # doesn't work
Direct modification of the params hash doesn't work either:
params[:role] = "account_admin" # doesn't work
Do I have to write this more like a controller test, with a direct call to post :create?
Capybara author jnicklas confirmed here that Capybara cannot make an app do things that are not available from the UI. He recommends controller tests for authorization.
However request specs written in RSpec without using Capybara syntax do allow direct use of HTML verbs (and some additional helpers) as outlined in the RSpec and Rails docs. So rather than Capybara's fill_in and click_link directives and the page object, you can use an attribute hash, verbs like get, post, post_via_redirect, and the response.body object. It's similar to a controller test, but you're using Rails' routing to choose the appropriate controller action based on the path provided. Here is an example of the latter technique:
describe "when standard user attempts to create account_admin user" do
let(:standard_user) { FactoryGirl.create(:standard_user) }
let(:attr) { { email: "account_admin#example.com",
password: "password",
password_confirmation: "password",
role: "account_admin" }
}
before do
login_as standard_user
get new_user_path
end
it "should not create a account_admin user" do
lambda do
post users_path, user: attr
end.should_not change(User, :count)
end
describe "after user posts invalid create" do
before { post_via_redirect users_path, user: attr }
# redirect to user's profile page
it { response.body.should have_selector('title', text: 'User Profile') }
it { response.body.should have_selector('div.alert.alert-error', text: 'not authorized') }
end
end

No request params in constraint when testing route

When I run this in practice it works, but I can't seem to write a working test for my route constraint with rspec.
When the test runs the constraint is triggered, but the request params are empty, thus it does not validate and the test fails.
I am running Rails 3.0.9, rspec-rails 2.6.1 and rspec 2.6.0.
config/routes.rb
match ":param1-unique-:param2" => "controller#index",
:constraints => ParamConstraint.new
lib/param_constraint.rb
class ParamConstraint
def matches?(request)
#request ||= request
valid_param1? && valid_param2?
end
def valid_param1?
#request.params[:param1] == "lorem"
end
def valid_param2?
#request.params[:param2] == "ipsum"
end
end
spec/routing/param_constraint_spec.rb
require 'spec_helper'
describe "param constraint routing" do
it "recognizes route for param1 and param2" do
{ :get => "/lorem-unique-ipsum" }.
should route_to(
:controller => "controller",
:action => "index",
:param1 => "lorem",
:param2 => "ipsum"
)
end
end
Update
If I inspect the request in the constraint I get the following output:
#<ActionDispatch::Request:0x007fee140ff910 #env={
"rack.version"=>[1, 1],
"rack.input"=>#<StringIO:0x007fee1446da48>,
"rack.errors"=>#<StringIO:0x007fee1446e768>,
"rack.multithread"=>true,
"rack.multiprocess"=>true,
"rack.run_once"=>false,
"REQUEST_METHOD"=>"GET",
"SERVER_NAME"=>"example.org",
"SERVER_PORT"=>"80",
"QUERY_STRING"=>"",
"PATH_INFO"=>"/lorem-unique-ipsum",
"rack.url_scheme"=>"http",
"HTTPS"=>"off",
"SCRIPT_NAME"=>"",
"CONTENT_LENGTH"=>"0"
}>
I ran into this same issue today, searching for an answer brought me to this page's question. For what it's worth, I had to resort to writing a request spec instead.
context "passing params that satisfy ParamConstraint" do
before do
visit "/lorem-unique-ipsum"
end
it "should serve up a page with content" do
# replace this with some assertion that gets satisfied by
# pages served up when ParamConstraint.new.matches? returns true
page.should have_selector("html body div#foo")
page.should_not have_selector("html body div#bar")
end
end
context "passing params that DO NOT satisfy ParamConstraint" do
before do
visit "/other-unique-other"
end
it "should serve up a page with different content" do
# replace this with some assertion that gets satisfied by
# pages served up when ParamConstraint.new.matches? returns false
page.should_not have_selector("html body div#foo")
page.should have_selector("html body div#bar")
end
end
This doesn't answer your question, which I take to be "how to test routing constraint", as the proper way would be via a routing spec. But given this gap in how request.params works when you use "should route_to", this is a workaround. A request spec, as opposed to a routing spec, will fill request.params correctly.
Same issue exists years later, with rspec-core 3.4.4, rspec-rails 3.4.2, rails 4.2.6. Don't have time to dig into exactly why...
You can use a request spec as suggested above, but don't use it to test the page contents. Instead, replicate a routing test (route_to) by checking the conversion of URL paths to request params:
RSpec.describe 'routes', type: :request do
describe '/:slug' do
it 'routes correctly' do
get '/test-product-slug'
expect(request.params).to eq(
'controller' => 'product',
'action' => :index,
'slug' => 'test-product-slug'
)
end
end
end

Rails 3 ajax response is not executed

I am creating a simple ajax feature into my application where a user can increase a "like count" through a link.
I have managed to get the link to run my controller code and the browser is getting a response back, but the response is not executed.
The like link is in partial:
.likes
.like_up
= link_to( image_tag('/images/thumb_up.png'), '/like/' + question.id.to_s, :remote => true)
#like_count
= 22# question.likecount
.like_down
= link_to( image_tag('/images/thumb_down.png'), '/dislike/' + question.id.to_s, :remote => true)
And it goes into likes_controller:
def up
#like = current_user.likes.create( :question_id => params[:question_id], :like => true )
respond_to do |format|
format.js
end
end
Then in my up.js.haml I simply have a debug javascript:
alert('2');
and nothing else.
When I press the like link in browser the rails log tels me that it has rendered the js.haml
...
Rendered likes/up.js.haml within layouts/application (104.9ms)
Completed 200 OK in 923ms (Views: 116.3ms | ActiveRecord: 20.1ms)
And when I look at the response the browser gets it certainly looks like the up.js.haml rendered into my application layout where the yield tag is placed.
<div class='content'>
alert('2');
</div>
I suspect that this is not what the browser wants to get back. Also when looking at the firebug console during the ajax request, nothing happens after the request is sent.
Could someone find out why this is not working? Every tutorial and guide I have found does this the same way I'm doing here.
First, make sure the content-type is correct:
curl --head -X POST http://localhost:8080/....
I believe the Content-Type should be text/javascript
Second, I think you want to disable the layout so that the DIV doesn't appear in the output.
def up
#like = current_user.likes.create( :question_id => params[:question_id], :like => true )
respond_to do |format|
format.js { render :partial => 'up', :layout => false }
end
end
Or maybe disable it in the whole controller with
layout false
It happened to me in a simple remote form.
Worked after restarting the server even it was the development environment.
Hope I save someone the hour I wasted.
Best regards.