In my Rails app, if a user wants to delete his own account he will first have to enter his password in my terminate view:
<%= form_for #user, :method => :delete do |f| %>
<%= f.label :password %><br/>
<%= f.password_field :password %>
<%= f.submit %>
<% end %>
This is my UsersController:
def terminate
#user = User.find(params[:id])
#title = "Terminate your account"
end
def destroy
if #user.authenticate(params[:user][:password])
#user.destroy
flash[:success] = "Your account was terminated."
redirect_to root_path
else
flash.now[:alert] = "Wrong password."
render :terminate
end
end
The problem is that I can't seem to find a way to test this with RSpec.
What I have is this:
describe 'DELETE #destroy' do
before :each do
#user = FactoryGirl.create(:user)
end
context "success" do
it "deletes the user" do
expect{
delete :destroy, :id => #user, :password => "password"
}.to change(User, :count).by(-1)
end
end
end
However, this gives me an error:
ActionView::MissingTemplate:
Missing template users/destroy, application/destroy with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder]}. Searched in:
* "#<RSpec::Rails::ViewRendering::EmptyTemplatePathSetDecorator:0x007fa7f51310d8>"
Can anybody tell me what I'm missing here or suggest a better way to test this action?
Thanks for any help.
OK, this is my solution:
describe 'DELETE #destroy' do
context "success" do
it "deletes the user" do
expect{
delete :destroy, :id => #user, :user => {:password => #user.password}
}.to change(User, :count).by(-1)
end
end
end
The before :each call I had before was useless (this is not an integration test after all). The password has to be passed in like this: :user => {:password => #user.password} which I didn't know until reading this thread.
Related
I have this integration test that is giving me errors that seem to have to do with fill_in
require 'rails_helper'
describe 'navigate' do
let(:user) {FactoryGirl.create(:user)}
let(:post) do
Post.create(date: Date.today, rationale: "Rationale", user_id: user.id)
end
before do
login_as(#user, :scope => :user)
end
describe 'index' do
before do
visit posts_path
end
it 'can be reached successfully' do
expect(page.status_code).to eq(200)
end
it 'has a title of Posts' do
expect(page).to have_content(/Posts/)
end
it 'has a list of posts' do
post1 = FactoryGirl.build_stubbed(:post)
post2 = FactoryGirl.build_stubbed(:second_post)
visit posts_path
expect(page).to have_content(/Rationale|content/)
end
it 'has a scope so that only post creators can see their posts' do
other_user = User.create(first_name: 'Non', last_name: 'Authorized', email: 'nonauth#example.com',
password: "password", password_confirmation: "password")
post_from_other_user = Post.create(date: Date.today, rationale: "This post shouldn't be seen",
user_id: other_user.id)
visit posts_path
expect(page).to_not have_content(/This post shouldn't be seen/)
end
end
describe 'new' do
it "has a link from the homepage" do
visit root_path
click_link("new_post_from_nav")
expect(page.status_code).to eq(200)
end
end
describe 'delete' do
it 'can be deleted' do
logout(:user)
delete_user = FactoryGirl.create(:user)
login_as(delete_user, :scope => :user)
post_to_delete = Post.create(date: Date.today, rationale: 'rationale', user_id: delete_user.id)
visit posts_path
click_link("delete_post_#{post_to_delete.id}_from_index")
expect(page.status_code).to eq(200)
end
end
describe 'creation' do
before do
visit new_post_path
end
it 'has a new form that can be reached' do
expect(page.status_code).to eq(200)
end
it 'can be created from a new form page' do
fill_in 'post[date]', with: Date.today
fill_in 'post[rationale]', with: "Some rationale"
click_on "Save"
expect(page).to have_content("Some rationale")
end
it 'will have a user associated with it' do
fill_in 'post[date]', with: Date.today
fill_in 'post[rationale]', with: "User Association"
click_on "Save"
expect(User.last.posts.last.rationale).to eq("User Association")
end
end
describe 'edit' do
it "can be edited" do
visit edit_post_path(post)
fill_in 'post[date]', with: Date.today
fill_in 'post[rationale]', with: "Edited content"
click_on "Save"
expect(page).to have_content("Edited content")
end
it "cannot be edited by a non authorized user" do
logout(:user)
non_authorized_user = FactoryGirl.create(:non_authorized_user)
login_as(non_authorized_user, :scope => :user)
visit edit_post_path(post)
expect(current_path).to eq(root_path)
end
end
end
I review the views/post/_form.html.erb file:
<%= form_for #post, class: "form-horizontal" do |f| %>
<% if #post.errors.any? %>
<% #post.errors.full_messages.each do |error| %>
<%= js add_gritter(error, title: "Overtime App Notification", sticky: false, image: :notice) %>
<% end %>
<% end %>
<div class="form-group">
<%= f.label :date, class: "col-sm-2 control-label" %>
<%= f.date_field :date, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :rationale, class: "col-sm-2 control-label" %>
<%= f.text_area :rationale, class: "form-control" %>
</div>
<%= render partial: 'status', locals: {f: f} if current_user.type == 'AdminUser' %>
<%= f.submit 'Save', class: 'btn btn-primary btn-block' %>
<% end %>
I can't tell what is going wrong here. If I inspect element, it's there:
This is my post.rb model file:
class Post < ActiveRecord::Base
enum status: {submitted: 0, approved: 1, rejected: 2}
belongs_to :user
validates_presence_of :date, :rationale
scope :posts_by, ->(user) {where(user_id: user.id)}
end
The tests were passing yesterday, not sure what happened.
You're calling login_as with #user, but #user is never defined, you probably want login_as(user).
Always try to retrace your steps, if something was working before, think about what code you introduced between the last time it worked and when it began failing.
Checking status codes is generally a bad smell for feature tests, just check for things the user can see.
Don't use the eq matcher with current_path instead use the have_current_path matcher provided by Capybara.
I`ve got these strange issues I dont know how to fix it. Please help me if you can.
Here is a testing result:
1) Admin can edit a hotel
Failure/Error: visit edit_admin_hotel_path(hotel)
URI::InvalidURIError:
bad URI(is not URI?):
# ./spec/requests/admin_spec.rb:32:in `block (2 levels) in <top (required)>'
2) Admin can edit a user
Failure/Error: visit edit_admin_user_path(admin)
URI::InvalidURIError:
bad URI(is not URI?):
# ./spec/requests/admin_spec.rb:54:in `block (2 levels) in <top (required)>'
rake routes shows me nice edit routes for users and hotels:
edit_admin_hotel GET /admin/hotels/:id/edit(.:format) admin/hotels#edit
edit_admin_user GET /admin/users/:id/edit(.:format) admin/users#edit
And everything works just fine if I start server and check it manually. So I have no idea where these issues comes from. Thanks for any help!
And my admin_spec.rb file:
require 'spec_helper'
describe "Admin" do
let(:admin) { FactoryGirl.create(:user) }
let(:hotel) { FactoryGirl.create(:hotel) }
before(:each) do
sign_up_as_admin admin
visit admin_hotels_path
end
subject { page }
it { expect(page).to have_content("Manage Hotels") }
it { expect(page).to have_content("Manage Users") }
it { expect(page).to have_link("Sign out") }
it { expect(page).to have_content("List of hotels") }
it { expect(page).to have_content("Hello, Admin") }
it "can add a hotel" do
click_link "Add Hotel"
expect(current_path).to eq(new_admin_hotel_path)
fill_in 'name', with: "TestHotel"
fill_in 'price', with: "666"
fill_in 'star_rating', with: "5"
expect { click_button "Submit" }.to change(Hotel,:count).by(1)
expect(current_path).to eq(admin_hotel_path(1))
end
it "can edit a hotel" do
visit edit_admin_hotel_path(hotel)
end
it "can delete a hotel" do
visit admin_hotel_path(hotel)
expect { click_link "Delete hotel" }.to change(Hotel,:count).by(-1)
#expect { click_link "Delete hotel" }.to redirect_to(admin_hotels_path)
end
it "can create a new user" do
click_link "Add User"
expect(current_path).to eq(new_admin_user_path)
expect(page).to have_content("Create New User")
fill_in "Name", :with => "user"
fill_in "Email", :with => "user#auser.com"
fill_in "Password", :with => "user.password"
fill_in "password_confirmation", :with => "user.password"
expect { click_button "Create User" }.to change(User,:count).by(1)
expect(current_path).to eq(admin_users_path)
end
it "can edit a user" do
visit edit_admin_user_path(admin)
end
end
Edit/update actions in users_controller.rb:
# GET admin/users/1/edit
def edit
#user = User.find(params[:id])
render "edit", status: 302
end
# PATCH/PUT admin/users/1
def update
#user = User.find(params[:id])
if #user.try(:update_attributes, user_params)
render "edit", notice: 'User was successfully updated.'
else
render action: 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :admin)
end
And user/edit.html.erb:
<% provide(:title, "Edit user") %>
<h1>Update profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for([:admin, #user]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes" %>
<% end %>
<%= button_to 'Delete User', [:admin, #user], :data => { confirm: 'Are you sure?' }, method: :delete %>
</div>
</div>
Update 1:
I found out that these bad URI(is not URI?) errors also in hotel_controller and comment_controller while testing edit action. These errors in all of my controllers in edit actions and I dont know what cousing them :(
Your edit action is wrong:
def edit
#user = User.find(params[:id])
render "edit", status: 302
end
A 302 status means that the response is a redirect and that the Location header in the response contains the URI (relative or absolute) to redirect to. Capybara will be looking for this header and probably winds up trying to do URI.parse(nil).
It's not clear to me why you are setting the status here at all
In your spec_helper have you included the url_helpers?
config.include Rails.application.routes.url_helpers
I'm working through Michael Hartl's Rails tutorial, and I'm running into an issue in section 7.3.3. I receive this error message:
ArgumentError in Users#new
Showing /Users/Anuraag/rails_projects/sample_app/app/views/users/new.html.erb where line #6 raised:
First argument in form cannot contain nil or be empty
when I run the following rspec:
bundle exec rspec spec/requests/user_pages_spec.rb -e "signup with invalid information"
at the following line:
<%= form_for(#user) do |f| %>
Here is my code. Please also let me know how I should improve this question if I'm not framing it well: I'm pretty new to StackOverflow as well
app/views/users/new.html.erb:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
app/assets/controllers/users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
# Handle a successful save.
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
spec/requests/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
end
In your new.html.erb you have specified form_for(#user) that means you need to have some value in instance variable #user before calling the new action.
You can do it in two ways :
one way is define an action new in your controller which would be called before rendering your new.html.erb layout. For eg:
def new
#user = User.new
end
other way is that in your form itself you could specify something like
<%= form_for(User.new) do |f| %>
Defining it in your new action is more standard way of doing it.
The order of actions in controller file matters. I had the same error until I figure out the problem on order of action methods.
Example.
Controller file
def edit
#post = Post.find(params[:id])
end
def update
end
private
def post_params
params.require(:post).permit(:title, :body)
end
I was working on edit action and was getting same error as you. The problem was I have written the edit action under private action. once I changed the order of this actions. It worked like a charm.
What I was trying to achieve ?
I was trying to take the values of post instance variable and use it into html form.
I dont see a new action on you controller.
def new
#user = User.new
end
I maybe made indirectly, but it couldn't hurt defining it yourself.
From you title that may be why the #user variable is nil, it wasn't defined.
How do you tweak the arguments that get passed in rspec's (v 2.6.1) post :create? Pointing me to rspec documentation that explains this command and how to use it would be fine with me. I've gone looking but can't find anything that answers my question.
I'm having difficulty writing rspec to behave properly for my Customer :create action that receives parameters, both for the Customer, as well as a different User object. More precisely, I'm unable to get the values in the hash I pass in the rspec post :create to behave like the input that comes from my form.
Please note that my Customer and User objects are separate ActiveRecords, and each Customer :belongs_to a User.
Here's my RSpec that's not doing what I want it to:
describe CustomersController do
render_views
describe "for signed-in admins" do
before(:each) do
#customer_attr = Factory.attributes_for(:customer)
#admin = Factory(:admin)
test_sign_in(#admin.user)
end
describe "POST 'create'" do
describe "success" do
it "should create a customer" do
lambda do
post :create, :customer => #customer_attr # The problem is here
end.should change(Customer, :count).by(1)
end
end
end
end
end
Here's the failure:
1) CustomersController for signed-in admins POST 'create' success should create a customer
Failure/Error: lambda do
count should have been changed by 1, but was changed by 0
# ./spec/controllers/customers_controller_spec.rb:323:in `block (5 levels) in <top (required)>'
The problem (as you'll see below) is obviously that post :create, :customer => #customer_attr doesn't include an argument that will end up in params[:user][:email], so the #user.save is failing because params[:user] is empty, but I've tried all kinds of ways to recompose the hash and pass it in that line but can't get it to work.
Here's the relevant controller code:
def create
#user = User.new(params[:user])
#user.user_type = 'customer'
#customer = Customer.new(params[:customer])
if #user.save
#user.customer = #customer
if #customer.save
# create stuff
redirect_to customer_path, :flash => { :success => "Customer created." }
else
# failure stuff
end
else
# more failure stuff
end
end
Here's the relevant form code (which behaves how I want it to):
<%= form_for(#customer) do |f| %>
<%= fields_for :user do |user_form| %>
<%= user_form.label :email %>
<%= user_form.text_field :email %>
<% end %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.submit "Create Customer Account" %>
<% end %>
Lastly, here's my factories code:
factory :user do |f|
f.sequence :email do |n|
"person#{n}#example.com"
end
f.password "thepassword"
f.password_confirmation "thepassword"
end
factory :customer do |f|
f.first_name "First"
f.last_name "Last"
f.user
after_build { |customer| customer.user.user_type = 'customer' }
end
The solution I found was to compose my hash to make it look exactly how I wanted, then pass it directly, without the :customer => in front of it.
before(:each) do
#customer = Factory(:customer)
#customer_attr = Factory.attributes_for(:customer)
#user_attr = {:user => Factory.attributes_for(:user)}
#customer_and_user_attr = {:customer => #customer_attr }.merge(#user_attr)
#admin = Factory(:admin)
test_sign_in(#admin.user)
end
Then in my controller I access items as e.g. params[:customer][:first_name] or params[:user][:email]
And the rspec looks like:
post :create, #customer_and_user_attr
So if I understand your question correctly, you just need to pass in another key to post, namely :user => #admin.user. Did I misunderstand you?
I am a beginning developer, and I was able to successfully use this tutorial (I'm in Rails 3): http://www.binarylogic.com/2008/11/16/tutorial-reset-passwords-with-authlogic/ to allow for users to reset their passwords, however I also have a customer model that I need to do this for, and I'm running into issues (listed below code). I think the error is my routing, but I'm not sure how to fix it.
routes.rb
resources :password_resets do
get 'edit_customer'
post 'edit_customer'
end
password_resets_controller.rb
class PasswordResetsController < ApplicationController
before_filter :load_user_using_perishable_token, :only => [:edit, :update]
before_filter :load_customer_using_perishable_token, :only => [:edit_customer, :update_customer]
before_filter :require_no_user, :require_no_customer
def new
render
end
def create
#user = User.find_by_email(params[:email])
#customer = Customer.find_by_email(params[:email])
if #user
#user.deliver_password_reset_instructions!
flash[:notice] = "Instructions to reset your password have been emailed to you. " +
"Please check your email."
redirect_to new_user_session_path
elsif
if #customer
#customer.deliver_customer_password_reset_instructions!
flash[:notice] = "Instructions to reset your password have been emailed to you. " +
"Please check your email."
redirect_to new_customer_session_path
end
else
flash[:notice] = "No account was found with that email address"
render :action => :new
end
end
def edit
render
end
def edit_customer
#redirect_to edit_customer_password_resets_path
end
def update
#user.password = params[:user][:password]
#user.password_confirmation = params[:user][:password_confirmation]
if #user.save
flash[:notice] = "Password successfully updated"
redirect_to new_user_session_path
else
render :action => :edit
end
end
def update_customer
#customer.password = params[:customer][:password]
#customer.password_confirmation = params[:customer][:password_confirmation]
if #customer.save
flash[:notice] = "Password successfully updated"
redirect_to new_customer_session_path
else
render :action => :edit
end
end
private
def load_user_using_perishable_token
#user = User.find_using_perishable_token(params[:id])
unless #user
flash[:notice] = "We're sorry, but we could not locate your account." +
"If you are having issues try copying and pasting the URL " +
"from your email into your browser or restarting the " +
"reset password process."
redirect_to new_user_session_path
end
end
def load_customer_using_perishable_token
#customer = Customer.find_using_perishable_token(params[:id])
unless #customer
flash[:notice] = "We're sorry, but we could not locate your account." +
"If you are having issues try copying and pasting the URL " +
"from your email into your browser or restarting the " +
"reset password process."
#redirect_to new_customer_session_path
end
end
end
MODELS
user.rb model
def deliver_password_reset_instructions!
reset_perishable_token!
UserMailer.password_reset_instructions(self).deliver
end
customer.rb model
def deliver_customer_password_reset_instructions!
reset_perishable_token!
UserMailer.customer_password_reset_instructions(self).deliver
end
VIEWS
password_resets/new
<% form_tag password_resets_path do %>
<p>Email:</p>
<%= text_field_tag "email" %><br />
<br />
<%= submit_tag "Reset my password" %>
<% end %>
password_resets/edit
<% form_for #user, :url => password_reset_path, :method => :put do |f| %>
<%= f.label :password %><br />
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Change my password and log me in"%>
<% end %>
password_resets/edit_customer
<% form_for #customer, :url => password_reset_path, :method => :put do |f| %>
<%= f.label :password %><br />
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Change my password and log me in" %>
<% end %>
It works perfectly for the code that does not have "customer" appended to it. The token generates, and I can get the email to send with the same format for the url: /password_resets//edit_customer
However, it receives an error when I try to pull up the url. With the code above, I receive this error: "No route matches {:action=>"show", :controller=>"password_resets"}" - I can also see that it interprets the :id to be the id of the controller (which is different than how the user one works - it correctly interprets the id as the token.
I have experimented a lot:
change :id in controller for edit_customer in load_customer_using_perishable_token to perishable_token, customer.perishable_token, #customer.perishable_token)
tried to append these to "get 'edit_customer'" using the path helper (=> "cust_pass") - :path, :path_name, :as so i could change the url to cust_pass_path
i even tried to add this to the routes just to see if it would work
match '/password_resets/:id/edit_customer' => 'password_resets#edit_customer'
So I stumped. Any help would be greatly appreciated!
Ok by my interpretation you are recieving the email, but clicking the link gives you an error? To start with the error message
"No route matches {:action=>"show", :controller=>"password_resets"}"
is appearing because you are trying to call the show action in your password_resets contoller, but it is not defined. First thing you should do is exclude it from your routes.
resources :password_resets, :except => :destroy do
get 'edit_customer'
post 'edit_customer'
end
I dont think it will fix your problem, but having routes that go nowhere is kind of pointless. Secondly make sure you do not have anything linking to the show action. As the error seems to be occuring when you click the link in the email I would think that the link you are sending is not correct.
I was able to fix this by updating the url in edit_customer.rb
from
:url => password_reset_path
to
:url => {:action=>"update_customer", :controller=>"password_resets"}
I also had to update my route.rb
from
resources :password_resets do
get 'edit_customer'
post 'edit_customer'
end
to
resources :password_resets, :except => [:index, :destroy, :show] do
get 'edit_customer'
put 'update_customer'
end