Hartl Chapter 11 Ajax undefined local variable or method `cookies' - ruby-on-rails

I am doing the Hartl Tutorial, and am currently in Chapter 11.2.5 A working follow button with AJAX.
My tests are failing for the Relationship Controller and I can't figure out why. The expected behavior happens in the browser. If I remove the cookies method it causes many other tests to fail. What am I missing that is causing this issue?
Here are my tests for the RelationshipController
require 'spec_helper'
describe RelationshipsController do
let(:user) { FactoryGirl.create(:user) }
let(:other_user) { FactoryGirl.create(:user) }
before { sign_in user, no_capybara: true }
describe "creating a relationship with Ajax" do
it "should increment the Relationship count" do
expect do
xhr :post, :create, relationship: { followed_id: other_user.id }
end.to change(Relationship, :count).by(1)
end
it "should respond with success" do
xhr :post, :create, relationship: { followed_id: other_user.id }
expect(response).to be_success
end
end
describe "destroying a relationship with Ajax" do
before { user.follow!(other_user) }
let(:relationship) { user.relationships.find_by(followed_id: other_user) }
it "should decrement the Relationship count" do
expect do
xhr :delete, :destroy, id: relationship.id
end.to change(Relationship, :count).by(-1)
end
it "should respond with success" do
xhr :delete, :destroy, id: relationship.id
expect(response).to be_success
end
end
end
Here is my Utilities.rb
include ApplicationHelper
RSpec::Matchers.define :have_error_message do |message|
match do |page|
expect(page).to have_selector('div.alert.alert-error', text: message)
end
end
def sign_in(user, options={})
if options[:no_capybara]
# Sign in when not using Capybara.
remember_token = User.new_remember_token
cookies[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
else
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
end
Here is my relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :signed_in_user
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
Here is my tests output:
Failures:
1) RelationshipsController creating a relationship with Ajax should increment the Relationship count
Failure/Error: before { sign_in user, no_capybara: true }
NameError:
undefined local variable or method `cookies' for # <RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fc7f827f930>
# ./spec/support/utilities.rb:13:in `sign_in'
# ./spec/models/relationship_spec.rb:8:in `block (2 levels) in <top (required)>'
2) RelationshipsController creating a relationship with Ajax should respond with success
Failure/Error: before { sign_in user, no_capybara: true }
NameError:
undefined local variable or method `cookies' for # <RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fc7f8516a90>
# ./spec/support/utilities.rb:13:in `sign_in'
# ./spec/models/relationship_spec.rb:8:in `block (2 levels) in <top (required)>'
3) RelationshipsController destroying a relationship with Ajax should decrement the Relationship count
Failure/Error: before { sign_in user, no_capybara: true }
NameError:
undefined local variable or method `cookies' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2:0x007fc7f85367f0>
# ./spec/support/utilities.rb:13:in `sign_in'
# ./spec/models/relationship_spec.rb:8:in `block (2 levels) in <top (required)>'
4) RelationshipsController destroying a relationship with Ajax should respond with success
Failure/Error: before { sign_in user, no_capybara: true }
NameError:
undefined local variable or method `cookies' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2:0x007fc7f8556938>
# ./spec/support/utilities.rb:13:in `sign_in'
# ./spec/models/relationship_spec.rb:8:in `block (2 levels) in <top (required)>'
Finished in 0.06635 seconds
4 examples, 4 failures
Failed examples:
rspec ./spec/models/relationship_spec.rb:12 # RelationshipsController creating a relationship with Ajax should increment the Relationship count
rspec ./spec/models/relationship_spec.rb:18 # RelationshipsController creating a relationship with Ajax should respond with success
rspec ./spec/models/relationship_spec.rb:29 # RelationshipsController destroying a relationship with Ajax should decrement the Relationship count
rspec ./spec/models/relationship_spec.rb:35 # RelationshipsController destroying a relationship with Ajax should respond with success

I noticed you put your test of RelationshipsController in spec/models. Is that really what the tutorial says to do? Try putting it under spec/controllers so it is treated as a controller spec.

I have just been struggling with this issue, as well as a lot of other people by the looks of things.
My solution (which I suspect is not the right one) was to change the line :
before { sign_in user, no_capybara: false }
to
before { sign_in user }
Not sure why, but it looks like my rspec test can not see the code in utilities.rb

Related

Devise RSpec Error

I am trying to validate that the current_user's organization matches that of the organization they are trying to view.
Here's the part of the controller that's failing this test (#organization is being defined in an earlier method):
if current_user.organization != #organization
redirect_to root_path, notice: "Not authorized to edit this organization"
end
Here's the failing test:
require 'rails_helper'
RSpec.describe Admin::PagesController, :type => :controller do
describe 'GET #home' do
login_user
before do
#organization = FactoryGirl.create(:organization)
end
context "valid params" do
it "renders the home template and returns http 200" do
get :home, name: #organization.name
expect(response).to render_template("home")
expect(response.status).to eq(200)
end
end
end
Here's my factory:
factory :user do
email { Faker::Internet.email }
organization_id 1
password "foobarfoobar"
password_confirmation { |u| u.password }
end
...And here's where login_user is being defined:
module ControllerMacros
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
Stacktrace:
1) Admin::PagesController GET #home valid params renders the home template and returns http 200
Failure/Error: it "renders the home template and returns http 200" do
expecting <"home"> but rendering with <[]>
# ./spec/controllers/admin/pages_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
However:
[2] pry(#<RSpec::ExampleGroups::AdminPagesController::GETHome::ValidParams>)> subject.current_user.organization == #organization
=> true
Not sure what is going wrong here, seems like pretty standard stuff. Any ideas?
Turns out the issue was that I was sending in the wrong parameter - should have been sending #organization.subdomain, not #organization.name. :(

NoMethodError: in Rspec

I'm currently doing rspec testing on two files and I got these failures regarding undefined methods. I need more calcification on how to exactly fixes these errors,Thanks!
Failures:
1) FavoritesController#create creates a favorite for the current user and specified post
Failure/Error: #post = create(:post)
NoMethodError:
undefined method `create' for #<RSpec::ExampleGroups::FavoritesController::Create:0x007fdabc84f7f8>
# /Users/bryanporras/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/testing/assertions/routing.rb:171:in `method_missing'
# ./spec/controllers/favorites_controller_spec.rb:8:in `block (2 levels) in <top (required)>'
2) FavoritesController#destroy destroys the favorite for the current user and post
Failure/Error: #post = create(:post)
NoMethodError:
undefined method `create' for #<RSpec::ExampleGroups::FavoritesController::Destroy:0x007fdabfe0f3e8>
# /Users/bryanporras/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/testing/assertions/routing.rb:171:in `method_missing'
# ./spec/controllers/favorites_controller_spec.rb:8:in `block (2 levels) in <top (required)>'
3) VotesController#up_vote adds an up-vote to the post
Failure/Error: sign_in #user
NoMethodError:
undefined method `sign_in' for #<RSpec::ExampleGroups::VotesController::UpVote:0x007fdabfe26fe8>
# /Users/bryanporras/.rvm/gems/ruby-2.2.1/gems/actionpack-4.2.1/lib/action_dispatch/testing/assertions/routing.rb:171:in `method_missing'
# ./spec/controllers/votes_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
4) Vote after_save calls `Post#update_rank` after save
Failure/Error: vote = Vote.new(value: 1, post: post)
NameError:
undefined local variable or method `post' for #<RSpec::ExampleGroups::Vote::AfterSave:0x007fdabdb8b0b0>
# ./spec/models/vote_spec.rb:25:in `block (3 levels) in <top (required)>'
Finished in 0.78639 seconds (files took 2.82 seconds to load)
15 examples, 4 failures, 2 pending
Failed examples:
rspec ./spec/controllers/favorites_controller_spec.rb:14 # FavoritesController#create creates a favorite for the current user and specified post
rspec ./spec/controllers/favorites_controller_spec.rb:24 # FavoritesController#destroy destroys the favorite for the current user and post
rspec ./spec/controllers/votes_controller_spec.rb:8 # VotesController#up_vote adds an up-vote to the post
rspec ./spec/models/vote_spec.rb:24 # Vote after_save calls `Post#update_rank` after save
This is the favorites_controller_spec.rb file:
require 'rails_helper'
describe FavoritesController do
include Devise::TestHelpers
before do
#post = create(:post)
#user = create(:user)
sign_in #user
end
describe '#create' do
it "creates a favorite for the current user and specified post" do
expect( #user.favorites.find_by(post_id: #post.id) ).to be_nil
post :create, { post_id: #post.id }
expect( #user.favorites.find_by(post_id: #post.id) ).not_to be_nil
end
end
describe '#destroy' do
it "destroys the favorite for the current user and post" do
favorite = #user.favorites.where(post: #post).create
expect( #user.favorites.find_by(post_id: #post.id) ).not_to be_nil
delete :destroy, { post_id: #post.id, id: favorite.id }
expect( #user.favorites.find_by(post_id: #post.id) ).to be_nil
end
end
end
and this is the votes_controller_spec.rb file:
require 'rails_helper'
describe VotesController do
include TestFactories
describe '#up_vote' do
it "adds an up-vote to the post" do
request.env["HTTP_REFERER"] = '/'
#user = authenticated_user
#post = associated_post
sign_in #user
expect {
post( :up_vote, post_id: #post.id )
}.to change{ #post.up_votes }.by 1
end
end
end
Check if you have config.include FactoryGirl::Syntax::Methods inside rails_helper.rb
If you have above line in rails_helper.rb file you can always use FactoryGirl.create :user
You didn't probably include include Devise::TestHelpers in specs for VotesController that's why it does not see sign_in method

Unable to test the session[:user_id] value set in POST #create in SessionsController spec with RSpec/Rails

I'm struggling with how to correctly test the POST #create action in the SessionsController. I've attempted to stub authentication. My assumption is that a valid test should test that the session[:user_id] is in fact equal to the #user.id value, after the post :create action is executed. However, I get nil returned for session[:user_id].
Mocks, stubs, etc are still a bit new to me. The stub for authentication seems pretty straightforward. However, why can't I get the session value to be returned when I run this test?
The test for valid authentication is currently failing (though it works in actual practice - i.e. I can sign in to the app). This is the one I'm concerned with. The second test (invalid password) is passing, and seems fine. All of my other session controller specs are passing.
Here's the portion of my sessions_controller_spec.rb file that deals with the session creation via POST #create:
require 'rails_helper'
describe SessionsController, type: :controller do
describe "POST #create" do
context "where authentication is valid" do
it "creates a new session with a welcome message" do
#user = create(User, id: 1)
allow(#user).to receive(:authenticate).and_return #user
post :create, email: "test#example.com", password: "secret1234"
expect(session[:user_id]).to eq #user.id
expect(flash[:notice]).to match("Welcome back!")
end
end
context "where password is invalid" do
it "re-renders the signin page with an alert message" do
user = create(:user)
post :create, session: { email: user.email, password: 'invalid' }
expect(response).to render_template :new
expect(flash[:alert]).to match("Incorrect email/password combination!")
end
end
end
# CODE FOR OTHER TESTS OMITTED
end
This is my sessions_controller.rb file:
class SessionsController < ApplicationController
def new
end
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
flash[:notice] = "Welcome back!"
redirect_to root_path
else
flash.now[:alert] = "Incorrect email/password combination!"
render :new
end
end
def destroy
session[:user_id] = nil
redirect_to new_session_path, notice: "Signed Out!"
end
end
The test returns the following error:
1) SessionsController POST #create where authentication is valid creates a new session with a welcome message
Failure/Error: expect(session[:user_id]).to eq #user.id
expected: 1
got: nil
(compared using ==)
# ./spec/controllers/sessions_controller_spec.rb:12:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:42:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:41:in `block (2 levels) in <top (required)>'
Is there a better, or preferred, way to test that the SessionsController successfully creates the session and sets the session[:user_id] value?
Comments, code critiques, etc, are welcome.
You didn't stub the method correctly:
allow(#user).to receive(:authenticate).and_return #user
In the controller you are calling authenticate on a class, not the instance. The line above should be:
allow(User).to receive(:authenticate).and_return #user

Pre-processing within a Rails Controller create method does not work

I am following Michael Hartl's tutorial, and trying to implement the reply twitter-like functionality, ie. "#122-john-smith: hello there" should be a reply to user 122.
I first tried filtering the "#XXX-AAA-AAA" part using a before_filter, but I decided to try it first in the very same Micropost#create action. So far I've got this MicropostController:
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
before_filter :correct_user, only: [:destroy]
#before_filter :reply_to_user, only: [:create]
def index
end
def create
#micropost=current_user.microposts.build(params[:micropost])
#Rails.logger.info "hoooola"
regex=/\A#(\d)+(\w|\-|\.)+/i
message=#micropost.content.dup
isResponse=message.match(regex)[0].match(/\d+/)[0]
#micropost.response=isResponse
if #micropost.save
flash[:success]="Micropost created!"
redirect_to root_path
else
#feed_items=[]
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_path
end
private
def correct_user
#micropost = current_user.microposts.find_by_id(params[:id])
redirect_to root_path if #micropost.nil?
end
def reply_to_user
regex=/\A#(\d)+(\w|\-|\.)+/i
#I use [0] cause the output of match is a MatchData class with lots of bs
mtch=params[:micropost][:content].match(regex)[0]
#puts mtch
##micropost=current_user.microposts.build(params[:micropost])
if mtch != nil
user_id=mtch.match(/\d+/)[0]
#replied_user=User.find(user_id)
#micropost.response=user_id unless #replied_user.nil?
end
end
end
And this is the snippet test I'm trying to pass:
require 'spec_helper'
describe "MicropostPages" do
subject { page }
let(:user) { FactoryGirl.create(:user) }
before { valid_signin user }
describe "micropost creation" do
before { visit root_path }
describe "with invalid information" do
it "should not create a micropost" do
expect { click_button "Post" }.should_not change(Micropost,
:count)
end
describe "error messages" do
before { click_button "Post" }
it { should have_content('error') }
end
end
describe "with valid information" do
before { fill_in 'micropost_content', with: "Lorem ipsum" }
it "should create a micropost" do
expect { click_button "Post" }.should change(Micropost,
:count).by(1)
end
end
end
...
end
If I run these tests I get the follwing error:
Failures:
1) MicropostPages micropost creation with invalid information should not create a micropost
Failure/Error: expect { click_button "Post" }.should_not change(Micropost, :count)
NoMethodError:
undefined method `[]' for nil:NilClass
# ./app/controllers/microposts_controller.rb:14:in `create'
# (eval):2:in `click_button'
# ./spec/requests/micropost_pages_spec.rb:11:in `block (5 levels) in <top (required)>'
# ./spec/requests/micropost_pages_spec.rb:11:in `block (4 levels) in <top (required)>'
2) MicropostPages micropost creation with invalid information error messages
Failure/Error: before { click_button "Post" }
NoMethodError:
undefined method `[]' for nil:NilClass
# ./app/controllers/microposts_controller.rb:14:in `create'
# (eval):2:in `click_button'
# ./spec/requests/micropost_pages_spec.rb:14:in `block (5 levels) in <top (required)>'
However if I modify the tests and comment out all the #XXX filtering in the Micropost#create action:
def create
#micropost=current_user.microposts.build(params[:micropost])
#Rails.logger.info "hoooola"
#regex=/\A#(\d)+(\w|\-|\.)+/i
#message=#micropost.content.dup
#isResponse=message.match(regex)[0].match(/\d+/)[0]
##micropost.response=isResponse
if #micropost.save
flash[:success]="Micropost created!"
redirect_to root_path
else
#feed_items=[]
render 'static_pages/home'
end
end
The tests pass just fine and the new Micropost is not a Nil object.
It can't seem to find an explanation here.
The error comes from this line:
isResponse=message.match(regex)[0].match(/\d+/)[0]
Check if your two match calls actually match correctly. If the pattern is not found in your string, nil is returned and the [0] call is made on nil. There's two instances in this line alone where this could happen.
Try to spread it out over several lines and check the return values of your matches or extend your Regex to properly check the pattern in one go.

NoMethodError: undefined method `email' for nil:NilClass (Rspec/factory girl/cancan/devise)

I am following Hartl's Rails tutorial but I used devise/cancan/rolify so some things are not the same.
On my users_pages_spec.rb test, I keep getting this error in spite of all I tried:
1) User pages index delete links as an admin user
Failure/Error: sign_in #superadmin
NoMethodError:
undefined method `email' for nil:NilClass
# ./spec/support/utilities.rb:14:in `sign_in'
# ./spec/requests/user_pages_spec.rb:91:in `block (5 levels) in <top (required)>'
2) User pages index delete links as an admin user
Failure/Error: sign_in #superadmin
NoMethodError:
undefined method `email' for nil:NilClass
# ./spec/support/utilities.rb:14:in `sign_in'
# ./spec/requests/user_pages_spec.rb:91:in `block (5 levels) in <top (required)>'
3) User pages index delete links as an admin user should be able to delete another user
Failure/Error: sign_in #superadmin
NoMethodError:
undefined method `email' for nil:NilClass
# ./spec/support/utilities.rb:14:in `sign_in'
# ./spec/requests/user_pages_spec.rb:91:in `block (5 levels) in <top (required)>'
4) User pages index delete links as an admin user
Failure/Error: sign_in #superadmin
NoMethodError:
undefined method `email' for nil:NilClass
# ./spec/support/utilities.rb:14:in `sign_in'
# ./spec/requests/user_pages_spec.rb:91:in `block (5 levels) in <top (required)>'
I don't understand why, does nil::nilClass mean factory girl actually does not manage to create this superadmin user?
Here is the sample of my user_pages_spec.rb file:
require 'spec_helper'
require 'cancan/matchers'
# Test if targeted words appears in displayed content
describe "User pages" do
subject { page }
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
let(:non_admin) { FactoryGirl.create(:user) }
let(:submit) { "Sign in" }
describe "index" do
describe "delete links" do
it { should_not have_link('delete') }
describe "as an admin user" do
let(:superadmin) { FactoryGirl.create(:superadmin) }
before do
sign_in #superadmin
visit users_path
end
it { should have_title('List of users') }
it { should have_link('delete', href: user_path(User.first)) }
it "should be able to delete another user" do
expect { click_link('delete') }.to change(User, :count).by(-1)
end
it { should_not have_link('delete', href: user_path(superadmin)) }
end
end
end
end
my spec/utilities.rb defining sign_in method
include ApplicationHelper
def sign_in(user)
visit new_user_session_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Login"
#populate cookie when not using capybara
cookies[:authentication_token] = user.authentication_token
end
and my spec/factories/user.rb
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "beta"
password_confirmation "beta"
# required if the Devise Confirmable module is used
confirmed_at Time.now
confirmation_token nil
factory :superadmin do
after(:create) {|user| user.add_role(:superadmin)}
end
end
end
I do have in cancan/rolify a superadmin role defined.
If anybody has a clue, I'd take it! i can update the question with additional file needed if not already here.
Your let call is setting superadmin, but you're referencing #superadmin, which is going to be nil, since it hasn't been set. BTW, this is a good reason to generally avoid using instance variables in your tests, as they silently default to nil when referenced if uninitialized.

Resources