I'm stuck at Chapter 9 in the Rails tutorial - more specifically at the end of section 9.1. My problem is similar to the one in this thread but the solution there didn't work for me.
Here is my user_pages_spec.rb:
require 'spec_helper'
describe "User pages" do
subject { page }
describe "signup page" do
before { visit signup_path }
it { should have_content('Sign up') }
it { should have_title(full_title('Sign up')) }
end
describe "profile page" do
let (:user) {FactoryGirl.create(:user)}
before { visit user_path(user) }
it { should have_content(user.name) }
it { should have_title(user.name) }
end
describe "signup" do
before { visit signup_path }
let(:submit) { "Create my account" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
describe "edit" do
let(:user) { FactoryGirl.create(:user)}
before do
sign_in user
visit edit_user_path(user)
end
describe "page" do
it { should have_content("Update your profile")}
it { should have_title("Edit user")}
it { should have_link('change', href:'http://gravatar.com/emails')}
end
describe "with invalid information" do
before { click_button "Save changes"}
it { should have_content('error') }
end
describe "with valid information" do
let(:new_name) { "New Name"}
let(:new_email) {new#example.com}
before do
fill_in "Name", with: new_name
fill_in "Email", with: new_email
fill_in "Password", with: user.password
fill_in "Confirm Password", with: user.password
click_button "Save changes"
end
it {should have_title(new_name)}
it {should have_selector('div.alert.alert-success')}
it {should have_link('Sign out', href: signout_path)}
specify {expect(user.reload.name).to eq new_name}
specify {expect(user.reload.email).to eq new_email}
end
end
end
Here is the error message:
bundle exec rspec spec/
.............................................FFFFFFFFF
Failures:
1) User pages edit page
Failure/Error: sign_in user
NoMethodError:
undefined method `sign_in' for #<RSpec::Core::ExampleGroup::Nested_4::Nested_4::Nested_1:0x007faa37859d80>
# ./spec/requests/user_pages_spec.rb:49:in `block (3 levels) in <top (required)>'
And here is my spec/support/utilities.rb:
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
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]
user.update_attribute(:remember_token, User.digest(remember_token))
else
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
end
Any suggestions?
I did run bundle install while I was trying to install rcov (Rubymine was complaining about its lack). The installation failed as rcov is not available for my version of rails. That's fine.
The really bizarre thing is that afterwards I re-ran the tests and everything worked. There was no trace of the error message. I am a rails noob but this is a bit rich - a failed bundle install shall not change anything. There was no reason for the error message I observed and now it disappeared without any reason.
==EDIT: I realised the problem is that rails does not seem to empty the cache between tests (which is, in my opinion, a scary bug). By default it fails to re-read the files and thus may ignore changes that have occurred. I put more details here: Rails tutorial: undefined method
Related
I have a person model that is the base for all types of users in my Rails 4 app. I have an administrator model, a worker model, a client model that all accept nested attributes. Here's my administrator model
class Administrator < ActiveRecord::Base
belongs_to :person
accepts_nested_attributes_for :person
... other model code here
end
In my SQLite database I have a constraint that says person_id cannot be null.
The controller test for the administrator is failing with the message'
ActiveRecord::StatementInvalid: SQLite3::ConstraintException: NOT NULL
constraint failed: administrators.person_id: INSERT INTO "administrators"
("created_at", "updated_at") VALUES (?, ?)
administrators_controller_test.rb contains the following,
setup do
#administrator = administrators(:administrator_montgomery_burns)
#person = people(:person_montgomery_burns)
end
test "should create administrator" do
assert_difference('Administrator.count') do
post :create,
administrator: {
person_id: #administrator.person_id
}
end
assert_redirected_to administrator_path(assigns(:administrator))
end
I've also tried,
test "should create administrator" do
assert_difference('Administrator.count') do
post :create,
administrator: {
person: {
first_name: #person.first_name,
... all other person attributes
}
}
end
assert_redirected_to administrator_path(assigns(:administrator))
end
Any ideas how this should be done?
I've solved it. I had a look at the log file params hash in the controller in development mode, (where it is working), and in test mode, (where it isn't). Instead of
test "should create administrator" do
assert_difference('Administrator.count') do
post :create,
administrator: {
person: {
first_name: #person.first_name,
... all other person attributes
}
}
end
assert_redirected_to administrator_path(assigns(:administrator))
end
use
test "should create administrator" do
assert_difference('Administrator.count') do
post :create,
administrator: {
person_attributes: {
first_name: #person.first_name,
... all other person attributes
}
}
end
assert_redirected_to administrator_path(assigns(:administrator))
end
Just change person: to person_attributes:.
SessionController action ends in redirect_to root_path.
Doing this
def sign_in(user)
visit new_session_path
fill_in "Username or email", with: user.email
fill_in "Password", with: 'please'
click_button "Sign in"
expect(page).to have_content "Sign out"
end
gives correct redirection:
Redirected to http://www.example.com/
Completed 302 Found in 4ms (ActiveRecord: 0.2ms)
Started GET "/" for 127.0.0.1 at 2014-11-28 01:08:13 -0800
Processing by Homepages::SponsorsController#index as HTML
...
But this
def sign_in(user)
page.driver.post session_path,
'session[credential]' => user.email,
'session[password]' => 'please'
end
Doesn't redirect correctly:
Redirected to http://www.example.com/
Completed 302 Found in 12ms (ActiveRecord: 0.2ms)
Why?
In every form generated by the form_for helper method, there is a hidden field named authenticity_token that contains a token used to prevent XSS attacks.
I suspect this could be the reason you're having problems posting directly to this controller.
You need to replace the page.driver with Capybara.current_session.driver:
def sign_in(user)
driver = Capybara.current_session.driver
driver.submit :post, session_path,
'session[credential]' => user.email,
'session[password]' => 'please'
end
Hello friends I have something to confess first,my previous account was banned from asking questions,From now on, i will try to make the questions more clear and precise!
I'm working on Hartl's ruby on rails tutorials ,i have been stuck at chapter 9.2.2 Requiring the right user `"Listing 9.13 Testing that the edit and update actions require the right user" for a few days. I have done lots of research,i went back and forth of the chapters , it didn't work and it seems no one had the issue that i have now . Let me explain in details.
Errors:
Authentication authorization as wrong user submitting a GET request to the Users#edit action
Failure/Error: specify { expect(response.body).not_to match(full_title('Edit user')) }
TypeError:
wrong argument type nil (expected Regexp)
# ./spec/requests/authentication_pages_spec.rb:61:in `block (5 levels) in <top (required)>'
Finished in 1.77 seconds
64 examples, 1 failure
i have tested with the brower,it works perfectly,i tried to edit other users. the page was directed to the home page successfully!
Github: https://github.com/Snailseason2014/Sample
here are some related files:
spec/requests/authentication_pages_spec.rb
require 'spec_helper'
describe 'Authentication' do
subject { page }
describe 'signin page' do
before { visit signin_path }
it { should have_content('Sign in') }
it { should have_title('Sign in') }
end
describe 'signin' do
before { visit signin_path }
describe 'with invalid information' do
before { click_button 'Sign in' }
it { should have_title('Sign in') }
it { should have_selector('div.alert.alert-error', text: 'Invalid') }
describe 'after visiting another page' do
before { click_link 'Home' }
it { should_not have_selector('div.alert.alter-error') }
end
end
describe 'with valid information' do
let(:user) { FactoryGirl.create(:user) }
before { sign_in user }
it { should have_title(user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Settings', href: edit_user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
describe 'followed by signout' do
before { click_link 'Sign out' }
it { should have_link('Sign in') }
end
end
end
describe 'authorization' do
describe 'for non-signed-in users' do
let(:user) { FactoryGirl.create(:user) }
describe 'in the Users controller' do
describe 'visiting the edit page' do
before { visit edit_user_path(user) }
it { should have_title('Sign in') }
end
describe 'submitting to the update action' do
before { patch user_path(user) }
specify { expect(response).to redirect_to(signin_path) }
end
end
end
describe 'as wrong user' do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: 'wrong#example.com') }
before { sign_in user, no_capybara: true }
describe 'submitting a GET request to the Users#edit action' do
before { get edit_user_path(wrong_user) }
specify { expect(response.body).not_to match(full_title('Edit user')) }
specify { expect(response).to redirect_to(root_url) }
end
describe 'submitting a PATCH request to the Users#update action' do
before { patch user_path(wrong_user) }
specify { expect(response).to redirect_to(root_url) }
end
end
end
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = 'welcome'
redirect_to #user
else
render 'new'
end
end
def edit
# #user = User.find(params[:id])
end
def update
# #user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = 'Profile updated'
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
# Before filters
def signed_in_user
redirect_to signin_url, notice: 'Please sign in.' unless signed_in?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
end
It looks like you skipped some of the exercises, specifically the exercises in section 5.6, which would have prevented your error.
In any case, in chapter 5 the tutorial had you define a duplicate full_title() helper for the tests to use, which was to be put in the file:
spec/support/utilities.rb
The original full_title() helper was used by the views.
Looking at your spec/support/utilities.rb file, you have this:
def full_title(page_title)
base_title = 'Ruby on Rails Tutorial Sample App'
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
Can you see what's wrong? It's obvious that you copy and pasted that code from the text of the tutorial, and you missed the last line which was: end, which is required to close the def you started on the first line. However, I cannot explain why you haven't been getting a SyntaxError when trying to run your tests, which prevents the tests from running at all when I try it.
In your spec:
expect(response.body).not_to match(full_title('Edit user'))
a match exepctation is expected to match some text against a regular expression eg:
expect("hello").to match(/ell/) # => true
expect("hello").to match(/blah/) # => false
full_title('Edit user') is not a regular expression... it's some content on the page. So it really isn't the right thing to use in an expect...match. You can turn any string into a regular expression by putting it inside // and using string-interpolation syntax eg:
a_string = 'some string'
a_regex = /#{a_string}/
so here you could use:
expect(response.body).not_to match(/#{full_title('Edit user')}/)
HOWEVER... the error message you get is indicative of something deeper... it says that you are passing a nil instead of a regular expression... which means that full_title('Edit user') is evaluating to nil instead of an actual string.
if you use my example above... the spec will likely still fail... so you have to figure out why full-title('Edit user') is returning nil and fix that first.
I'm stuck (again!) at Chapter 9 (this time in section 9.2.2) of the Rails tutorial. I am getting
bundle exec rspec spec/
................................FFF........................
Failures:
1) Authentication authorization as wrong user submitting a GET request to the Users#edit action
Failure/Error: before {sign_in user, no_capybara: true}
NoMethodError:
undefined method `new_remember_token' for #<User:0x007f8181815448>
# ./spec/support/utilities.rb:13:in `sign_in'
# ./spec/requests/authentication_pages_spec.rb:71:in `block (4 levels) in <top (required)>'
The other 2 errors are of the same type.
Here is spec causing the errors:
describe "as wrong user" do
let(:user) {FactoryGirl.create(:user)}
let(:wrong_user) {FactoryGirl.create(:user, email: "wrong#example.com")}
before {sign_in user, no_capybara: true}
describe "submitting a GET request to the Users#edit action" do
before {get edit_user_path(wrong_user)}
specify { expect(response.body).not_to match(full_title('Edit user'))}
specify { expect(response).to redirect_to(root_url)}
end
describe "submitting a PATCH request to the Users#update action" do
before { patch user_path(wrong_user)}
specify { expect(response).to redirect_to(root_url)}
end
end
And here is the method (utilities.rb) the error message is complaining about:
def sign_in (user, options={})
if options[:no_capybara]
# Sign in when not using Capybara
remember_token = User.new_remember_token
cookies[:remember_token]
user.update_attribute(:remember_token, User.digest(remember_token))
else
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
end
The code for the model (User.rb) is here:
class User < ActiveRecord::Base
before_save { self.email = email.downcase}
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, length: {minimum: 6}
has_secure_password
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
I had previously trouble with the sign_in method but it miraculously disappeared. What am I doing wrong?
I finally found the culprit for the erratic test results that I have been observing in this case and, quite likely, on previous occasions (Failure/Error: sign_in user undefined method `sign_in', Rails named route not recognized). The problem seems to be that rails does not clear by default the cache between tests. Which is, actually, downright scary. It seems you cannot really trust the test results. I realised this by commenting out the method that rails was complaining about and re-running the test. The error persisted which meant one thing - rspec was simply working with some cached versions of the files and thus disregarding the changes which I am making. So even if the tests pass you can't be sure that they really do. This is really bizarre. After realising the problem with a bit of googling I found how to force rails to clean the cache - check jaustin's answer here: is Rails.cache purged between tests?
I have Rails 4.2.6 and rspec 3.3.0 , devise version 3.5.8 and i have trouble to test my controller specs always getting error saying ("expected the response to have a success status code (2xx) but it was 401")
Later when i try different spec I am always getting failure message ("Your account is not enabled yet !"), any ideas ?, any help much appreciated ..
my spec:
require 'rails_helper'
describe MyController, :type => :controller
let(:user) { create(:user) }
let(:campaign) { create(:campaign, user: user) }
let!(:placement) { create(:placement, user: user, campaign: campaign, end_date: Date.today) }
before(:each) do
sign_in user
end
context "when we have no data" do
before do
get :graph_data, format: :json
#json = JSON.parse(response.body)
end
it "should be a success" do
expect(response).to have_http_status(:success)
end
end
end
I have following spec_helper:
config.include Devise::TestHelpers, :type => :controller
config.before(:each) do
DatabaseCleaner.start
end
factory defined:
FactoryGirl.define do
factory :unconfirmed_user, class: User do
sequence(:email) { |n| "john#{n}#email.com" }
sequence(:name) { |n| "John Nice #{n}" }
password 'password'
password_confirmation 'password'
factory :user do
confirmed_at Time.now.utc
factory :admin do
organisation { create(:organisation, :some_organisation) }
end
end
end
controller:
MyController < ApplicationController
def graph_data
items = current_user.items //outputs [1,2,3,45,5]
render json: items, status: :ok
end
end
routes:
GET /mycontroller/graph_data(.:format)
i found what was the issue,on User model had a method 'active_for_authentication?'
def active_for_authentication?
super && (group.include?('company-group) || is_admin? )
end
Method 'active_for_authentication?' is a Devise public method
and when it is defined in your model like (User), you change behaviour of it, and in my case it was overwritten with different behaviour,
checking for belonging user to the 'company-group' or not ..
That's why i had failure message ("Your account is not enabled yet !")