Rspec controller error? expecting <"index"> but rendering with <""> or it's working? - ruby-on-rails-3

I'm new with rspec test and maybe there are something that I dont undertand.
if can any help me, I really appreciate some help.
File Structure:
app/models/booking.rb
app/models/user.rb
app/models/role.rb
app/models/ability.rb
app/controllers/bookings_controller.rb
app/views/bookings/index.html.erb
app/views/dashboard/index.html.erb
app/spec/controllers/bookings_controller_spec.rb
I read this link with a similar problem but it isn't solved
Rspec controller error expecting <"index"> but rendering with <"">
is similar, because if I change this line:
it 'should not render index template from bookings' do
get :index
=> response.should_not render_template(:index)
end
for this other:
it 'should not render index template from bookings' do
get :index
=> response.should render_template(:index)
end
I get the same mistake that in the link
expecting <"index"> but rendering with <"">
and I don't know why?
Here's my Code:
My Spec:
describe BookingsController do
context 'as guest' do
before(:each) do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.save
#when i save, with gem CanCan i assign a default role to #user
#with the default role the user only can see the views/dashboard/index.html.erb
end
it 'should not render index template from bookings' do
get :index
response.should_not render_template(:index)
end
end
end
Controller:
class BookingsController < ApplicationController
load_and_authorize_resource
def index
...
end
def show
...
end
end
My model:
class Booking < Activerecord::Base
paginates_per 20
def
...
end
def
...
end
end
User:
Class User < ActiveRecord::Base
after_save :set_default_role
rolify
.
.
.
.
def set_default_role
self.add_role :default
end
end
Role:
class Role < ActiveRecord::Base
ROLES = {"admin" => "Admin", "default" => "Default"}
.
.
.
.
scopify
end
Ability:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :data_consistency
can :read, Booking
end
end
end

CanCan authorizes model access not controller actions. For most other actions these two are more or less the same thing, but not for the index. On the index action CanCan adds a scope to the query for records that includes your authorization restrictions.
What this means is that your guest user will simply not be able to see any records, but the view will still render.
What you want is authentication (ie Devise) and use it from a before_filter in each controller that requires an authenticated user to access.
class BookingsController < ApplicationController
load_and_authorize_resource # Handles authorization
before_filter !authenticate_user # Handles authentication (included with Devise)
...
end

In my case, the problem was solved in before(:each) block!
My code works like this:
before :each do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.confirm!
sign_in #user
end

Related

Clearance failure when forbidden password reset

I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.
I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.
I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.
class UsersController < Clearance::UsersController
before_action :require_login, only: [:create] # does this need to be in both user controllers?
...
def user_params
params.require(:user)
end
end
And here is the clearance controller.
class Clearance::UsersController < ApplicationController
before_action :require_login, only: [:create]
require 'will_paginate/array'
def new
#user = user_from_params
render template: 'users/new'
end
def create
#user = user_from_params
#user.regenerate_password
if #user.save
sign_in #user unless current_user
UserMailer.welcome_email(#user).deliver!
redirect_to users_path
else
render template: 'users/new'
end
end
def edit
#user = User.friendly.find(params[:id])
end
def update
#user = User.friendly.find(params[:id])
if #user.update(permit_params)
redirect_to #user
flash[:success] = "This profile has been updated."
else
render 'edit'
end
end
private
def avoid_sign_in
redirect_to Clearance.configuration.redirect_url
end
def url_after_create(user)
dashboards_path(user)
end
def user_from_params
user_params = params[:user] || Hash.new
is_public = check_public_params(user_params)
first_name = user_params.delete(:first_name)
last_name = user_params.delete(:last_name)
email = user_params.delete(:email)
password = user_params.delete(:password)
parish = user_params.delete(:parish)
division = user_params.delete(:division)
admin = user_params.delete(:admin)
Clearance.configuration.user_model.new(user_params).tap do |user|
user.first_name = first_name
user.last_name = last_name
user.password = password
user.email = email
user.is_public = is_public
user.parish_id = parish.to_i
user.division = division
user.admin = admin
end
end
def permit_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
end
end
EDIT: relevant portions of routes.rb
Rails.application.routes.draw do
resources :passwords, controller: "clearance/passwords", only: [:create, :new]
resource :session, controller: "clearance/sessions", only: [:create]
resources :users, controller: "clearance/users", only: [:create] do
resource :password,
controller: "clearance/passwords",
only: [:create, :edit, :update]
end
get "/sign_in" => "clearance/sessions#new", as: "sign_in"
delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
get "/sign_up" => "clearance/users#new", as: "sign_up"
constraints Clearance::Constraints::SignedOut.new do
root to: 'high_voltage/pages#show', id: 'landing'
end
constraints Clearance::Constraints::SignedIn.new do
# root to: 'dashboards#index', as: :signed_in_root
root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
end
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
# root to: 'teams#index', as: :admin_root
# end
resources :users do
collection { post :import }
end
It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using #user, but since I'm using FriendlyId I needed to change that to #user.id.
So instead of...
<%= link_to 'Change My Password', edit_user_password_url(#user, token: #user.confirmation_token.html_safe) %>
I did
<%= link_to 'Change My Password', edit_user_password_url(#user.id, token: #user.confirmation_token.html_safe) %>
Thanks, Thoughbot, for this great gem!

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

Rspec controller error: No route matches {:controller=>"bookings", :action=>"/dashboard/index"}

I'm getting an error when I try to test mi controller, I'm new with rspec test and maybe there are something that I dont undertand.
$rspec spec/controllers/booking_controller_spec.rb
I get the following error:
No route matches {:controller=>"bookings", :action=>"/dashboard/index"}
and I don't understand why? so if can any help me, I really appreciate some help.
File Structure:
app/models/booking.rb
app/models/user.rb
app/models/role.rb
app/models/ability.rb
app/controllers/bookings_controller.rb
app/views/bookings/index.html.erb
app/views/dashboard/index.html.erb
app/spec/controllers/bookings_controller_spec.rb
My fail Spec:
describe BookingsController do
context 'as guest' do
before(:each) do
#user = User.new(:email => 'mail_admin#test.com',
:username => 'admin',
:password => 'password_admin',
:password_confirmation => 'password_admin')
#user.save
#when i save, with gem CanCan i assign a default role to #user
#with the default role the user only can see the views/dashboard/index.html.erb
end
it 'should not render index template from bookings' do
get :index
response.should_not render_template(:index)
end
it 'should render index template from dashboard' do
get '/dashboard/index'
response.should render_template('index')
end
end
end
Controller:
class BookingsController < ApplicationController
load_and_authorize_resource
def index
...
end
def show
...
end
end
My model:
class Booking < Activerecord::Base
paginates_per 20
def
...
end
def
...
end
end
User:
Class User < ActiveRecord::Base
after_save :set_default_role
rolify
.
.
.
.
def set_default_role
self.add_role :default
end
end
Role:
class Role < ActiveRecord::Base
ROLES = {"admin" => "Admin", "default" => "Default"}
.
.
.
.
scopify
end
Ability:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :data_consistency
can :read, Booking
end
end
end

How do I emulate logging in for controller tests?

I have a SearchesController that requires a user to be logged in before it will do its thing.
I'd like to write an rspec helper function login to emulate logging in for controller tests. (NB: I will handle integration / requests specs separately.) My attempts so haven't worked: the logged_in? method in ApplicationController returns false.
The question: how do I write the 'login' helper?
Here's the RSpec controller test:
# file: spec/controllers/searches_controller_spec.rb
require 'spec_helper'
require 'controllers_helper'
describe SearchesController do
include ControllersHelper
describe "GET index" do
it 'without login renders login page' do
get :index
response.should redirect_to(login_path)
end
it 'with login finds searches belonging to user' do
me = FactoryGirl.create(:user)
my_searches = FactoryGirl.create_list(:search, 2, :user => me)
not_me = FactoryGirl.create(:user)
not_my_searches = FactoryGirl.create_list(:search, 2, :user => not_me)
login(me) # want to define this in spec/controllers_helper.rb
get :index
assigns(:searches).should =~ my_searches
end
end
end
Here's the Controller:
# file: app/controllers/searches_controller.rb
class SearchesController < ApplicationController
def index
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
else
#searches = Search.where(:user_id => current_user.id)
respond_to do |format|
format.html
format.json { render json: #searches }
end
end
end
end
And here's the ApplicationController code. Note that current_user = x has the effect of logging x in, and it's rather simple: it sets #current_user and session[:user_id].
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
protected
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
#current_user = user
session[:user_id] = user && user.id
end
def logged_in?
!!#current_user
end
def require_login
unless logged_in?
redirect_to login_path, :alert => "You must be logged in to access this page."
end
end
helper_method :current_user, :logged_in?, :require_login
end
I may have said this before, but if Stack Overflow gave badges answering one's own questions, I'd have a LOT of badges! :)
Okay, to answer this question you need to look at the documentation for ActionController::TestCase. When you do so, you'll find that it sets up bindings for:
#controller
#request
#response
So for the specific controller given in the OP, writing the login method is trivial:
# file: spec/controllers_helper.rb
module ControllersHelper
def login(user)
#controller.send(:current_user=, user)
end
end
(Did I hear someone say RTFM again? I thought so...)

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