Why is capybara/rspec losing the current_user? - ruby-on-rails-3

The following spec is failing and failing over again. I've tried everything but can't get it to work. If I test it manually all looks fine :( Some help/tips would be really nice!
The join action should add an logged-in user to a guild/group if he/she has the right token (in the URL). If the user isn't logged in, the action redirects to the login page and saves the token and the ID to cookies. After login the user gets redirected to the join page if the cookies are set.
I've found out that the current_user get lost during the test. The session variable is still present. I've a standard Authlogic setup and all other tests are passing so I really don't know what's going wrong. I'm new to RSpec/capybara but the cucumber/capybara test (from which I'm migrating) is also failing so I think it's a capybara issue.
Failing Spec:
describe GuildsController do
fixtures :roles
def login
#user = Factory(:User)
visit login_path
fill_in 'Login', :with => #user.login
fill_in 'Password', :with => 'password'
click 'Login'
page.should have_css(".notice")
end
def assing_user_to_guild_as(role)
role_id = Role.where(:name => role).first.id
#guild.assignments << Assignment.new(:role_id => role_id, :user_id => #user.id, :guild_id => #guild.id)
end
before(:each) do
#guild = Guild.first || Factory(:Guild).build
visit root_path
end
context "a user" do
before(:each) do
login
end
it "should be able to join a guild with a valid token" do
visit "guilds/#{#guild.id}/join/#{#guild.token}"
#guild.members.include?(#user.login).should be_true
page.should have_css(".notice")
end
it "shouldn't be able to join a guild with a invalid token" do
visit "guilds/#{#guild.id}/join/#{#guild.token+"invalid"}"
#guild.members.include?(#user.login).should be_false
page.should have_css(".error")
end
end
end
Controller Action:
def join
#guild = Guild.find(params[:id])
respond_to do |format|
if current_user.nil?
flash[:error] = t("have_to_be_logged_in")
unless params[:token].nil?
cookies[:rguilds_jg_token] = params[:token]
cookies[:rguilds_jg_gid] = params[:id]
end
format.html { redirect_to(login_path) }
else
unless cookies[:rguilds_jg_token].nil? && cookies[:rguilds_jg_gid].nil?
cookies.delete(:rguilds_jg_token)
cookies.delete(:rguilds_jg_gid)
end
if #guild.verified?
if params[:token] == #guild.token
unless #guild.users.include?(current_user)
#guild.assignments << Assignment.create(:user_id => current_user.id, :role_id => Role.find_by_name("member").id)
flash[:notice] = t('guilds.joined')
format.html { redirect_to(#guild) }
else
flash[:error] = t('guilds.already_joined')
format.html { redirect_to(#guild) }
end
else
flash[:error] = t('guilds.invalid_token')
format.html { redirect_to(#guild) }
end
else
flash[:error] = t('guilds.not_verified')
format.html { redirect_to(#guild) }
end
end
end
end
"rake spec" result:
...................FF.....................................................................
Failures:
1) GuildsController a user should be able to join a guild with a valid token
Failure/Error: #guild.members.include?(#user.login).should be_true
expected false to be true
# ./spec/integration/guilds_spec.rb:72:in `block (3 levels) in <top (required)>'
2) GuildsController a user shouldn't be able to join a guild with a invalid token
Failure/Error: page.should have_css(".error")
expected #has_css?(".error") to return true, got false
# ./spec/integration/guilds_spec.rb:79:in `block (3 levels) in <top (required)>'
Finished in 7.87 seconds
90 examples, 2 failures
Gems:
gem 'rails', '3.0.0.rc'
gem "mocha"
gem "rspec-rails", ">= 2.0.0.beta.19"
gem "factory_girl_rails"
gem 'capybara'
gem "authlogic", :git => "http://github.com/odorcicd/authlogic.git", :branch => "rails3"

# In your test_helper.rb / spec_helper.rb
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
This is from http://gist.github.com/470808

Related

Rspec Devise Sign in

I trying to sign in my admin so as to test the controller specs in rspec
I did my best to follow the instruction mention over here but it seem that I missed something because of which the admin is still not able to login resulting in my controller spec to fail
Here my code
## spec/spec_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
## spec/support/controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:admin]
admin = Admin.create(first_name: 'Admin',
last_name: 'User',
email: 'admin#poaster.me',
password: 'admin1234',
password_confirmation: 'admin1234',
active: true
)
admin.confirmed_at = Time.now
admin.confirm!
sign_in admin
## Did an idependent test over here using binding.pry not sure why
# binding.pry
# session => {"warden.user.admin.key"=>[[27], "$2a$04$6KicZPZUvYxOBiMadVyIqe"]}
# request['env'].warden.user => nil
# request['env'].warden.authenticated? => false
end
end
end
The output that see in session inside my controller spec
{"flash"=>
#<ActionDispatch::Flash::FlashHash:0x007f9bee570738
#closed=false,
#flashes={:alert=>"You have to confirm your account before continuing."},
#now=nil,
#used=#<Set: {}>>}
Controller Spec
## spec/controllers/users_controller_spec.rb
login_admin
describe UsersController do
context 'GET users/index' do
it 'render users index pages' do
get :index
#binding.pry
#session => {"flash"=> #<ActionDispatch::Flash::FlashHash:0x007f9bee570738 #closed=false,#flashes={:alert=>"You have to confirm your account before continuing."},#now=nil,#used=#<Set: {}>>}
expect(response).to render_template('index')
end
end
end
Controller Code
## app/controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_admin!
before_filter :require_admin?, :only => [:deactivate, :activate, :index]
def index
page = params[:page] || 1
#users = User.includes(:company).where(roles_mask: 1) params[:page])
#users = #users.page(page).per(10)
end
end
Found it seem the issue was because I was doing this
admin.confirmed_at = Time.now
I turn using only
admin.confirm! plainly work out of box

Request spec with Devise + Rspec2 + Spork/Guard

I have setup Rspec2 + Spork + Guard + Devise
My files are as follows
#spec_helper.rb
Spork.prefork do
#code
Dir[Rails.root.join('spec/support/**/*.rb')].each {|f| require f}
RSpec.configure do |config|
config.extend ControllerMacros, :type => :controller
end
end
Spork.each_run do
# This code will be run each time you run your specs.
FactoryGirl.reload
include ControllerMacros
end
#spec/support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mapping[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
end
#spec/support/devise.rb
Spec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
in my request spec
#spec/features/documents_spec.rb
require 'spec_helper'
describe "Documents" do
login_user
describe "GET /documents" do
it "should display document name as sameera CV" do
#spec code
end
end
end
and when I run bundle exec guard, I get
1) Documents GET /documents should display document name as sameera CV
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# ./spec/support/controller_macros.rb:4:in `block in login_user'
So far I have done lots of fixes via google and nothing seems to be working, can someone help me :)
I'm on
rails 3.2.9
rspec 2.12.0
devise 2.2.3
any help would be greatly appreciated
Try changing #request.env["devise.mapping"] = Devise.mapping[:user] to request.env["devise.mapping"] = Devise.mapping[:user] in spec/support/controller_macros.rb
Here I'm answering my own question, and I was able to find a workaround for the question I asked.
Following are the steps I did
1) removed the controller_macros.rb and devise.rb from support directory
2) removed the ControllerMacros references from spec_helper.rb
3) Added the following code to
#spec/features/documents_spec.rb
before(:each) do
user = FactoryGirl.create(:user)
visit root_path
fill_in 'user_email', :with => user.email
fill_in 'user_password', :with => user.password
click_button 'Sign in'
end
I'm sure there should be a more elegant way (as describe in devise wiki), but this WORKS :)

Rails 3 error in Safari only - ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):

In following Railscast #274 to get reset password working in my Rails 3 app, I am experiencing a weird issue in Safari. If I run my app in Heroku I get the following error when I go to my root:
ActiveRecord::RecordNotFound (Couldn't find User with auth_token = ):
app/controllers/application_controller.rb:39:in `lookup_user'
app/controllers/application_controller.rb:32:in `current_user'
app/controllers/application_controller.rb:54:in `logged_in?'
app/controllers/users_controller.rb:8:in `new'
If use Firefox and Chrome (in incognito mode) it works. In Safari, I found that if I get the error, I can make it go away by navigating to /logout. Then the page renders perfectly.
Here's my route for /logout and root:
match "/logout" => "sessions#destroy", :as => "logout"
root :to => "users#new"
Here's my destroy action in sessions_controller:
def destroy
reset_session
cookies.delete(:auth_token)
redirect_to root_path, :notice => "You successfully logged out"
end
My application_controller:
protected
def current_user
#current_user ||= lookup_user
end
def lookup_user
if session[:user_id]
User.find_by_id(session[:user_id])
elsif cookies[:auth_token]
User.find_by_auth_token!(cookies[:auth_token])
end
end
And lastly, here's my new action in users_controller:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
end
end
What I've tried:
To alter the new action to delete cookies with the following:
def new
#user = User.new
#user.profile = Profile.new
if logged_in?
redirect_to profile_path(current_user)
elsif
cookies.delete(:auth_token)
end
end
The rake task below, as suggested in the Railscast comments:
namespace :user do
desc "Rebuild Auth-Tokens"
task :rebuild_auth_token => :environment do
User.transaction do
User.all.each { |u|
u.generate_token(:auth_token)
u.save!
}
end
end
end
(I ran this with `heroku run rake user:rebuild_auth_token`)
Neither seems to have worked. Can anyone help me figure this out?
Anytime you regenerate the user :auth_code's you will need to delete your cookies for that domain. In a production, you should not regenerate :auth_codes and you will never have this issue, unless users edit their cookies.
In addition I have posted a response on the railscast.com authentication (revised) solution so Ryan can take a look at it.
Good luck!

Ruby Problemewith filter and RSpec

I can't figure out why this RSpec test fails. Any advice?
I try to handle the destruction of post. Only users who create posts can delete them.
class PostsController < ApplicationController
before_filter :authorized_user, :only => :destroy
def destroy
post = Post.find(params[:id])
post.destroy
redirect_to posts_path, notice: 'Post successfully destroyed'
end
private
def authorized_user
redirect_to posts_path, notice: 'Access Denied' if current_user.posts.find_by_id(params[:id]).nil?
end
Test :
describe "DELETE destroy" do
before(:each) do
#post = stub_model(Post, :id => 23)
#post.stub(:destroy){true}
Post.stub(:find){#post}
end
it "should search the post" do
Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
delete :destroy, {:id => #post.id }
end
it "should destroy the post" do
#post.should_receive(:destroy)
delete :destroy, {:id => #post.id }
end
it "should redirect to the posts list" do
delete :destroy, {:id => #post.id }
response.should redirect_to posts_path
end
end
And errors :
1) PostsController DELETE destroy should search the post
Failure/Error: Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
(<Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, user_id: integer) (class)>).find("23")
expected: 1 time
received: 0 times
# ./spec/controllers/posts_controller_spec.rb:67:in `block (3 levels) in <top (required)>'
I think it is because in the before(:each) block you have to log in a user. You have before_filter and this filter is only for :destroy. So when you don't have a logged in user then your tests will fail because it is actually a user without permissions that "runs the test". So you should put this code in before(:each):
user = way_to_make_user - I do it with: FactoryGirl.create(:user,role=>"client")
controller.sign_in user
I could help you more if you tell me what do you use for authentication. I use cancan gem. So the main point is that you have to log in user before each test.

rspec controller test with devise authentication

I am having problem with rspec testing controller the devise authentication.
I have a following setup
I have included
config.include Devise::TestHelpers, :type => :controller
in my spec_helper.rb
In my merchants_controller_spec.rb
describe MerchantsController do
before :each do
#user = Factory(:user)
#merchant = Factory(:merchant, :user_id => #user.id,:is_approved => false, :is_blacklisted => false)
controller.stub!(:current_user).and_return(#user)
end
describe "GET index" do
it "assigns all merchants as #merchants" do
merchant = Factory(:merchant,:is_approved => true, :is_blacklisted => false)
get :index
assigns(:merchants).should eq([merchant])
end
end
end
My merchants_controller.rb
class MerchantsController < ApplicationController
before_filter :authenticate_user!
def index
#merchants = Merchant.approved
debugger
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #merchants }
end
end
end
I have a scope approved in merchant model
scope :approved, where(:is_approved => true, :is_blacklisted => false)
Now my problem is even though i stubbed current_user and returned #user as current_user, My merchants_controller index spec is failing. But if i comment out authenticate_user! then the spec passes,
without authenticate_user! the debugger of index action is caught but with authenticate_user! debugger is not caught.
I think there is problem in subbing current_user and i am not able to figure it out.
Help me out..
Have you read through the docs on github?:
Devise includes some tests helpers for functional specs. To use them, you just need to include Devise::TestHelpers in your test class and use the sign_in and sign_out methods. Such methods have the same signature as in controllers:
sign_in :user, #user # sign_in(scope, resource)
sign_in #user # sign_in(resource)
sign_out :user # sign_out(scope)
sign_out #user # sign_out(resource)
Another alternative
RSpec.describe YourController, :type => :controller do
before do
user = FactoryGirl.create(:user)
allow(controller).to receive(:authenticate_user!).and_return(true)
allow(controller).to receive(:current_user).and_return(user)
end
# rest of the code
end