how to specify page object when rails spec test? - ruby-on-rails

Now, I try to test login and page transition using rails spec.
to check session change via login, test needs "rack_session_access/capybara" library.
I check the session of login user is nil before login.
after the user logged in, I check the session of login user exists by searching session from user id. but, if I write this code(#2), the test doesn't work.(the error occurred at #3. error says can't find out Logout link).
If I comment out this checking session code(#2), the test works well.
Does anybody tell me why??
require 'rails_helper'
require "rack_session_access/capybara"
feature "session_test" do
background do
User.create(name: 'test_user', email: 'user#example.com', password: 'user', password_confirmation: 'user')
end
scenario "Login_and_Logout" do
key = User.find_by(email: 'user#example.com').id
# the session of login user should be empty
expect(page.get_rack_session["user_id"]).to eq nil
visit '/sessions/new'
within("form") do
fill_in 'session_email', with: 'user#example.com'
fill_in 'session_password', with: 'user'
end
# 1 user login
click_button 'Login'
# 2 the session should have user_id value.
expect(page.get_rack_session["user_id"]).to eq key
# 3 user logout
find('body > header > div > nav > ul > li:nth-child(4) > a').click
# 4 check transition
expect(page).to have_content 'This is the home page'
# 5 the session should be empty again
session = page
expect(page.get_rack_session["user_id"]).to eq nil
end
end

Related

Capybara loses cookies between requests

I have the follow test:
def login_user(email, password)
visit new_user_session_path
fill_in 'E-mail', with: email
fill_in 'Password', with: password
click_button 'go'
end
scenario 'some test' do
order = Fabricate(:order_one, company: user.company)
visit "http://127.0.0.1:49645/orders/#{order.id}" # change to visit "/orders/#{order.id}"
login_user(user.email, user.password)
#assert
end
Whats happen is that in first step (visit...) the user is not logged, so I set some informations using cookie. But, when login_user is executed, this cookie is empty.
Usigin selenium-webdriver
Any idea here ?
Thanks in advance
When using a JS capable driver #visit is not guaranteed to have finished loading the page when it returns. Since the cookie is not set in the browser until the response is processed, your login_user is actually telling the browser to stop processing it's current visit and visit somewhere else which stops the cookie from ever being set in the browser. After the first visit you need to wait for something visible on the page to be sure the cookies are correctly set.
scenario 'some test' do
order = Fabricate(:order_one, company: user.company)
visit "http://127.0.0.1:49645/orders/#{order.id}"
expect(page).to have_content('you are not logged in') #or whatever shows on the page visited
login_user(user.email, user.password)
#assert
end

Devise, looking for a good way to integration test the interaction between two users in a turn based system

Stack: Rails '4.0.4', devise, rSpec, factory_girl, cappybara + selenium-webdriver, mySQL
I'm stll finding myself a little confused controlling user auth in my tests, but this patchwork from other examples is working for now. I have a file called request_helpers.rb in /support that contains:
require 'spec_helper'
include Warden::Test::Helpers
module RequestHelpers
class Login
def self.create_logged_in_user
user = FactoryGirl.create(:user)
login(user)
user
end
def self.login(user)
login_as user, scope: :user, run_callbacks: false
end
end
end
And this is an example of a passing test:
require "spec_helper"
feature "Story Management" do
let( :authorized_user ){ RequestHelpers::Login.create_logged_in_user }
scenario "has a valid factory" do
authorized_user.should be_an_instance_of( User )
end
scenario "Can visit root", js:true do
visit root_path( authorized_user )
page.should have_content( "Your Stories" )
end
end
My question is, How can I logout my authorized user, and log in a new authorized user? Every attempt to utilize devise logout method in my request helper hasn't worked.
Here is my attempt at testing this:
require 'spec_helper'
include Warden::Test::Helpers
Warden.test_mode!
module RequestHelpers
class Login
def self.create_logged_in_user
user = FactoryGirl.create(:user)
login(user)
user
end
def self.login(user)
login_as user, scope: :user, run_callbacks: false
end
def self.logout(user)
logout( user )
end
end
end
scenario "Two users can take turns adding 3 chapters each" do
chapter_string = ValidString.short
player1 = create(:user)
player2 = create(:user)
RequestHelpers::Login.login(player1)
visit new_story_path( player1 )
fill_in "story_title", with: ValidString.short
fill_in "co_author", with: player2.email
click_button "Create Story"
click_link "New Chapter"
fill_in "chapter_body", with: chapter_string
click_button "Create Chapter"
page.should have_content(chapter_string)
RequestHelpers::Login.logout(player1)
RequestHelpers::Login.login(player2)
fill_in "chapter_body", with: chapter_string
click_button "Create Chapter"
page.should have_content(chapter_string)
end
Failed test text:
1) Chapter Management Two users can take turns adding 3 chapters each
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
# ./spec/support/request_helpers.rb:16
I decided to stop trying to hack around devise here, and just fill in the forms like they suggested in the docs. Logging in and out works fine in this scenario, although slower.
From Devise docs:
These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session.

Capybara, CanCan, Test vs. Dev

I have a Rails 3.2 app with Devise and CanCan. User abilities are defined by roles. There are five Roles in the database with id values 1-5, and a User has_one role through a Membership. So, for example, Membership.create(user_id: 2, role_id: 4) will lead to User.find(2).role == Role.find(4) # => true
My ability file looks like
if user.has_role? :admin
can :manage, Model
can :manage, AnotherModel
# etc.
elsif user.has_role? :basic
can :read, Model
can :read, AnotherModel
elsif user.has_role? (etc. for other roles)
(etc.)
else
cannot :manage, :all
I want to kick out people who are attempting to access pages they are not authorized for. My application controller includes:
rescue_from CanCan::AccessDenied do |exception|
reset_session
redirect_to new_user_session_path
end
This behavior works properly when clicking around in development—-a basic user created via rails console gets kicked out when trying to do admin things, and a user with no roles (created in console, then User.last.membership.destroy so that User.last.role # => nil) cannot log in to the dashboard--instead the user is immediately redirected to the login page (a User with no role cannot manage anything).
I'm having problems when trying to test this same behavior, specifically for users with no role:
# spec/support/login_helpers.rb
def login_via_sign_in_page(user)
visit new_user_session_path
fill_in :user_email, with: user.email
fill_in :user_password, with: user.password
click_button 'Sign in'
end
# spec/features/dashboard_spec.rb
describe "as a user with no role" do
let(:user) { FactoryGirl.create(:user) }
before { login_via_sign_in_page(user) }
it "should redirect to the sign_in page" do
current_path.should == new_user_session_path
end
end
The above test fails, with RSpec showing that the user successfully made it to the dashboard page instead of being redirected to the sign in page. However, the following specs do pass:
# spec/features/dashboard_spec.rb
describe "as a guest browser (no login whatsoever)" do
it "should redirect to the sign_in page" do
visit dashboard_index_path
current_path.should == new_user_session_path
end
end
describe "as an admin user" do
let(:user) { FactoryGirl.create(:admin_user) }
before { login_via_sign_in_page(admin_user) }
it "should go to the dashboard" do
visit dashboard_index_path
current_path.should == dashboard_index_path
end
end
...as well as things like basic user getting redirected to the sign_in page when attempting admin actions
I have read this post, which seems to be dealing with a similar issue, but the suggestions there have not helped.
Finally, I get confusing results when using debugger and save_and_open_page. The former will give me user # => <A legit User object> and user.role # => nil. However, save_and_open_page shows me logged in on the dashboard page.
Have you tried including the Warden::Test::Helpers, and the rest of the config required for testing Devise with Capybara? Take a look here How To: Test with Capybara. Which driver are you using?

Can't sign in with FactoryGirl's user/data

I have to sign in the user before doing my tests which will use JS. The sign in page does work (except on this test).
require 'spec_helper'
require 'capybara/poltergeist'
include Capybara::DSL
Capybara.javascript_driver = :poltergeist
describe "Application", js: true do
describe "when logged in", js: true do
let(:user) { FactoryGirl.create(:user) }
before do
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
it "can authenticate user" do
User.all.first.authenticate(user.password).email.should == user.email
end
end
end
The problem is that clicking the "Sign in" button returns back to the page with "invalid user/password" combination (as if the user is not in the db). The thing is that apparently the use really IS in the database and the password is right (the test "can authenticate user" passes).
I took a screenshot before clicking the "Sign in" button and the fields are populated correctly. I tried to create the user inside the before block but it didn't work either.
The sign in page doesn't have anything special, no javascript or anything. Just a normal form post to the server.
Any ideas?
this was happening to me a while ago while using rspec and poltergeist. The problem was that phantomjs thread wasn't able to see the record created by FactoryGirl in the db. To solve this problem, you have to configure your db connection to be shared in your spec_helper.rb adding the code below.
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
You can find more explanation here in the point number 3.

Using Cucumber with a Static database

I am trying to run Cucumber tests against a database that is already populated with data. It's basically a copy of our production database.
For some reason I have some tests failing which seemingly is coming from the fact that it can't find the data. Any idea on how to make this work and why it wouldn't like "pre-seeded" databases?
Our data is far too complex to try to recreate via factories/fixtures. There's just no way.
Here's an example:
Scenario: a tech logs in
Given I am on the login page
And user with id 2328 exists
When I login as "user" with password "password"
Then I should be on the dashboard page
And I should see "Logged in as: USER"
The step:
Given /^user with id (\d+) exists$/ do |id|
Employee.exists? id
end
When /^I login as "([^\"]*)" with password "([^\"]*)"$/ do |username, password|
visit login_url
fill_in "username", with: username
fill_in "pwd", with: password
click_button "Login"
end
Maybe its my steps and has nothing to do with the database, but I can't understand why it doesn't fail on the And user with id 2328 exists step when I give it a bad number.
I have seen this but it doesn't seem to affect the flow.
Lastly, here is the actual login method (believe me, I know how awful this login check is, simply checking we have permissions to the database):
def login
if request.post?
# get database config for env
configs = ActiveRecord::Base.configurations[Rails.env]
begin
# attempt to connect to the db
plsql = plsql_connect(params[:username], params[:pwd])
# if no exception, get the employee ID and location
session[:employee_id] = plsql.hes.installer_web_pkg.get_employee_pk.to_i
session[:employee_location] = plsql.hes.installer_web_pkg.fn_get_truck_location(session[:employee_id]).to_i
# logout
plsql.logoff
# dashboard time!
redirect_to dashboard_url(session[:employee_id].to_i)
rescue Exception => err
# could not connect, fail!!
flash.now[:notice] = "Username/password not valid (or locked out of account). Try again or contact the HelpDesk."
render :action => "login"
end
end
end
Every time I run, I get:
expected: "/dashboard",
got: "/login" (using ==) (RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/web_steps.rb:198:in `/^(?:|I )should be on (.+)$/'
features/tech_queue.feature:11:in `Then I should be on the dashboard page'

Resources