password Ruby on Rails Tutorial by M. Hartl - ruby-on-rails

I've just started using rails, and decided to follow the "Ruby on Rails Tutorial" by M. Hartl. Seems like a good intro.
Am running into a failed test that's driving me nuts.
I am running rails 3.1.1, with rspec 2.7.0
I have tried modifying the condition, and tests on the "has_password" method work.
The failing test:
1) User password encryption authenticate method should return the user on email/password match
Failure/Error: matching_user.should == #user
expected: #
got: nil (using ==)
# ./spec/models/user_spec.rb:149:in `block (4 levels) in '
The rspec test:
describe User do
before(:each) do
#attr = {:name => 'testing',
:email =>'testing#example.com',
:password => "testtest",
:password_confirmation => "testtest"}
end
...
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
...
describe "authenticate method" do
it "should exist" do
User.should respond_to(:authenticate)
end
it "should return nil on email/password mismatch" do
User.authenticate(#attr[:email], "wrongpass").should be_nil
end
it "should return nil for an email address with no user" do
User.authenticate("bar#foo.com", #attr[:password]).should be_nil
end
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
In the User model:
...
def has_password?(submitted_password)
encrypt_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email) #self.where("email = ?", email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
I cannot figure out what I'm doing wrong here.

In your failing spec you have
matching_user.should == #user
But #user isn't defined anywhere so it's set to nil.
Edit:
Try adding the following puts into the failing spec and see what results you get in your spec output after running it.
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
puts matching_user # add this
puts #user # and also this
matching_user.should == #user
end

Related

Rails Test Error, NameError: uninitialized constant User::FILL_IN

I am currently learning Ruby on Rails on Rails Tutorial.
I did rails test, and then got this error.
error log
UsersSignupTest#test_valid_signup_information_with_account_activation:
NameError: uninitialized constant User::FILL_IN
app/models/user.rb:42:in `activate'
app/controllers/account_activations_controller.rb:6:in `edit'
test/integration/users_signup_test.rb:39:in `block in <class:UsersSignupTest>'
I do not understand the meaning of this error. I have to initialize constant User::FILL_IN probably.
app/models/user.rb
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
....
def activate
update_columns(activated:FILL_IN, activated_at:FILL_IN)
end
app/controllers/account_activations_controller.rb
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
test/integration/users_signup_test.rb
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
...
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
log_in_as(user)
assert_not is_logged_in?
get edit_account_activation_path("invalid token", email: user.email)
assert_not is_logged_in?
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
Could you help me to solve this?
You are not supposed to actually use the words FILL_IN. You are supposed to change FILL_IN the whatever you need to make the tests pass. You probably need something like:
update_columns(activated: true, activated_at: Time.now)

Rspec Capybara - Login(user) not working

Hello i have a Rspec/Capybara test im trying to make.
Im a logged in user that as admin user should be the only user to add Sizes to my app.
I just can't get the test to Login the user. Can anyone see why?
Please look at the sessions controller below. I do have a log_in method.
Error is
Failures:
1) adding size allow a admin user to add a size
Failure/Error: log_in(admin)
NoMethodError:
undefined method `log_in' for #<RSpec::ExampleGroups::AddingSize:0x007f8e4fa828e0>
# ./spec/features/sizes_features.rb:9:in `block (2 levels) in <top (required)>'
Test
require "rails_helper"
RSpec.feature "adding size" do
let(:size01) { FactoryGirl.build :size01 }
let(:user) { FactoryGirl.build :user }
let(:admin) { FactoryGirl.create(:user, admin: true) }
scenario "allow a admin user to add a size" do
log_in(admin)
size = create(:size01)
visit new_size_path
fill_in 'Title', with: "example"
click_button 'Create Size'
expect(current_path).to eql(sizes_path)
expect(page).to have_content("example")
end
scenario "user can't add size" do
log_in(user)
visit sizes_path
expect(current_path).to eql(root_path)
expect(page).to have_content("Rescricted Web Page")
end
scenario "vistor can't add size" do
visit sizes_path
expect(current_path).to eql(root_path)
end
end
FactoryGirl
FactoryGirl.define do
factory :user, :class => User do
username "example"
email "example#example.com"
admin "false"
password_digest "<%= User.digest('password') %>"
activated "true"
activated_at "<%= Time.zone.now %>"
end
end
Sessions controller.
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the user corresponding to the remember token cookie.
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
#current_user = nil
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.url if request.get?
end
end
Here is why it's not working:
Access to session and request is not possible from the test, Access to
response is limited. Some drivers allow access to response headers and
HTTP status code, but this kind of functionality is not provided by
some drivers, such as Selenium.
source: Capybara documentation
You have two options:
If you're using Devise for authentication, Devise provides authentication helpers you should use.
Otherwise, here's how I would approach your situation:
Instead of trying to directly manipulate the session, create a shared context or a helper that logs in the user by interacting with the login form in the same way a user browsing your site would.
Here is one approach:
spec/support/when_authenticated.rb
RSpec.shared_context 'When authenticated' do
background do
authenticate
end
def authenticate
visit '/sessions/new'
within('form#session') do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Sign in'
end
end
Then, in your feature spec:
RSpec.feature 'User does something' do
include_context 'When authenticated'
# examples
end
This has the effect of running the authentication procedure before each example in your spec.

Rails + Cucumber/Capybara: How to set/retrieve cookies in tests?

I'm implementing a lazy login feature. My cucumber feature should describe it:
Feature: User log in
Scenario: Lazy login
Given I didn't log out the last time I was on the site
When I go to the homepage
Then I should automatically be logged in
And these are my step definitions:
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.reset_sessions!
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do #<-- Fails here
page.should have_content("Logout")
end
This is what happens when a user logs in: the cookies.signed[:auth_token] gets set. This will be used by a before filter in my ApplicationController so that users who open a fresh browser will be logged in automatically:
class SessionsController < Devise::SessionsController
def create
super
if user_signed_in?
puts 'yesssssss'
session[:user_id] = current_user.id
current_user.remember_me! if current_user.remember_token.blank?
cookies.signed[:auth_token] = {
:value => current_user.remember_token,
:domain => "mysite.com",
:secure => !(Rails.env.test? || Rails.env.development?)
}
puts "current_user.remember_token = #{current_user.remember_token}"
puts 'cookies:'
puts cookies.signed[:auth_token]
end
end
end
This is the before filter in my ApplicationController:
def sign_in_through_cookie
logger.info "logging in by cookie"
puts "logging in by cookie"
puts cookies.signed[:auth_token] #<-- PROBLEM: this returns nil.
return true if !current_user.nil?
if !cookies[:auth_token].nil? && cookies[:auth_token] != ''
user = User.find_by_remember_token(cookies.signed[:auth_token])
return false if user.blank?
sign_in(user)
puts 'success'
return true
else
return false
end
end
So the issue is that in the last step of my cucumber feature, cookies.signed[:auth_token] returns nil. I'm guessing this is just a capybara thing. So do I actually have to set a cookie in the test as opposed to using the one in my controller?
So eventually I figured it out after trying a lot of different things.
Given(/^I didn't log out the last time I was on the site$/) do
user = FactoryGirl.create(:user)
visit new_user_session_path
fill_in('user[email]', with: user.email)
fill_in('user[password]', with: 'test123')
click_button('Sign in')
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
When(/^I go to the homepage$/) do
visit root_path
end
Then(/^I should automatically be logged in$/) do
page.should have_content("Logout")
end
UPDATE:
Here's what I use in case I'm using Selenium for some of the tests:
if Capybara.current_session.driver.class == Capybara::Selenium::Driver
auth_token = page.driver.browser.manage.cookie_named('auth_token')[:value]
page.driver.browser.manage.delete_all_cookies
page.driver.browser.manage.add_cookie(:name => "auth_token", :value => auth_token)
else
puts "cookies = #{Capybara.current_session.driver.request.cookies}"
Capybara.current_session.driver.request.cookies.[]('auth_token').should_not be_nil
auth_token_value = Capybara.current_session.driver.request.cookies.[]('auth_token')
Capybara.reset_sessions!
page.driver.browser.set_cookie("auth_token=#{auth_token_value}")
end
Use https://github.com/nruth/show_me_the_cookies which wraps the driver methods. It has methods for getting cookies, deleting cookies, and a method for creating cookies called create_cookie.
I needed just to test the cookie values
Inspiration taken from https://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles
and ported to Rails 5.x
Create features/support/cookies.rb
With content
module Capybara
class Session
def cookies
#cookies ||= ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
end
end
end
Before do
allow_any_instance_of(ActionDispatch::Request).to receive(:cookie_jar).and_return(page.cookies)
allow_any_instance_of(ActionDispatch::Request).to receive(:cookies).and_return(page.cookies)
end
Then the step for testing
Then('is set cookie {string} with value {string}') do |cookie, value|
expect(page.cookies.signed[cookie]).to eq value
end

rspec before(:each) using factorygirl to create model not working

I'm using rspec to test my model methods. Everything is going fine but all of a sudden factorygirl build isn't working. here's my code:
require File.dirname(__FILE__) + '/../spec_helper'
describe User do
describe 'creation' do
before(:each) do
#user = FactoryGirl.build(:user)
end
it 'is invalid without an email address' do
user = #user
user.email = nil
user.should_not be_valid
end
it 'is invalid without a password' do
user = #user
user.password = nil
user.should_not be_valid
end
it 'is invalid with an invalid email' do
user = #user
user.email = "email#invalid"
user.should_not be_valid
end
it 'is valid with a valid email and password' do
user = #user
user.should be_valid
end
end
describe "method" do
before(:each) do
#user = FactoryGirl.build(:user)
end
context "first_name" do
it "should return the user's first name" do
user = #user
user.first_name.should == "User"
end
end
context "count_of_invoices_by_year" do
# #todo not sure how to check these since .count doesn't work
it "should return the correct count of all invoices in the specified year" do
# there are two invoices in 2013
# user = #user
# user.count_of_invoices_by_year("2013", :total).should == 2
end
it "should return the correct count of paid invoices in the specified year" do
user = #user
debugger
end
it "should return the correct count of sent invoices in the specified year" do
end
end
context "sum_of_invoices_by_year" do
it "should return the sum of the grand_totals of each of the invoices in the specified year" do
# user.sum_of_invoices_by_year("2013", :total).should ==
end
it "should return the sum of the grand_totals of each of the paid invoices in the specified year" do
end
it "should return the sum of the grand_totals of each of the sent invoices in the specified year" do
end
end
end
end
all the other #user are set correctly and work but when i get down here:
it "should return the correct count of paid invoices in the specified year" do
user = #user
debugger
end
the factorygirl.build just doesn't work. I tried putting it everyone including directly in the specific test...but it just won't set to #user. what am i missing? this is the error:
NameError Exception: undefined local variable or method `user' for #<RSpec::Core::Example:0x007fc9ec7d4890>
which happens when i try to look at user because #user is nil
Turns out user isn't actually present in a test case unless you actually try to test something with it...which is very weird.
it "should return the user's first name" do
user.first_name.should == "User"
end
this has user present but this:
it "should return the user's first name" do
debugger
end
looking at the console after debugger shows user = nil here.

Sorcery fails to log in in RSpec

I have an app that uses Sorcery (or tries to use it),
and I am writing the specs for it:
context "successfull attempts to log in" do
let(:attr) { attributes_for(:credentials) }
before(:each) do
#user = create(:user, attr)
end
it "should log the user in" do
post :create, attr.merge(remember_me: false)
controller.should be_logged_in
end
end
Here is the FactoryGirl factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.safe_email
password "password"
password_confirmation { |u| u.password }
client_id 1
end
factory :credentials, class: User do
email "user#example.com"
password "password"
end
end
And here is the controller action:
class SessionsController < ApplicationController
# ...
def create
login(params[:email], params[:email], params[:remember_me])
flash.now[:error] = "Invalid email/password combination"
render :new
end
end
The error message is the following:
1) SessionsController POST 'create' successfull attempts to log in should log the user in
Failure/Error: controller.should be_logged_in
expected logged_in? to return true, got false
# ./spec/controllers/sessions_controller_spec.rb:54:in `block (4 levels) in <top (required)>'
The spec keeps failing for some reason. Could anyone explain to me why please?
Your controller uses the email as both the username and password, which looks like a copy and paste error. Is that correct?

Resources