I'm in the process of splitting change-password functionality off into its own page on my site. The whole process works fine when I run through it manually as a real user, but I can't get my Cucumber tests to pass, the reason being that on the final call to the controller, the current_user is mysteriously returning nil.
I've having a serious WTF moment because the user DEFINITELY is being logged in at the beginning of the tests, and I'm following all the same steps as other passing tests, and as I said the whole thing works fine when stepping through it by hand.
Here's the test in question:
Scenario: The user should be able to change their password
Given I have a signed in user with email = "test#mycompany.com" and password = "password"
When I am on the change-password page # this hits registrations_controller#change_password
And I fill in "Current password" with "password"
And I fill in "New password" with "new_password"
And I fill in "Re-enter new password" with "new_password"
And I press "Update"
Then I should see "You updated your account successfully"
And the login step:
Given /^I have a signed in user with email\s*=\s*"([^"]*)" and password\s*=\s*"([^"]*)"$/ do |email, password|
#user = User.new
#user.email = email
#user.password = password
#user.confirm!
#user.save!
visit '/sign_in'
fill_in("Email", :with => #user.email)
fill_in("Password", :with => #user.password)
click_button("Sign in")
end
Controller actions, from "registrations_controller.rb":
def change_password
# calling "current_user" here retuns the logged-in user as expected
#user = current_user
end
def update_password
# calling "current_user" here returns nil
# error occurs at call to nil.update_with_password
#user = current_user
if #user.update_with_password( params["user"] )
redirect_to after_update_path_for( #user )
else
clean_up_passwords( #user )
render_with_scope :change_password
end
end
Now, we are using devise (1.1.8), and my best guess is that I've done something wrong with the devise routes, which look like this in "routes.rb"
devise_for :users, :controllers => {:confirmations => "confirmations", :sessions => "sessions", :registrations => "registrations"} do
# ...other routes here...
get "/change_password", :to => "registrations#change_password"
put "/update_password", :to => "registrations#update_password"
end
Finally, for completeness, here is "change_password.html.haml"
= render :partial => "shared/stats"
#main
= form_for(resource, :as => resource_name, :url => "update_password", :html => { :method => :put, :autocomplete => "off" }) do |f|
= devise_error_messages!
.edit-account-hold
%span Your password
= f.label :current_password, "Current password"
= f.password_field :current_password
= f.label :password, "New password"
= f.password_field :password
= f.label :password_confirmation, "Re-enter new password"
= f.password_field :password_confirmation
= f.submit "Update", :class => "orange-button border-radius"
= link_to "Cancel", account_path, :id => "cancel-link"
:javascript
$(document).ready( function() {
$("#user_current_password").focus();
});
Try running it through Selenium. Watching the test run can often give clues about what's going wrong.
Related
I first installed devise and altered it so I can login with username instead of email.
After that I entered the Activeadmin gem into my gem file.
After that I did a bundle install and rake active_admin:install.
But after trying to log into the backend /admin/login I see this error message :
undefined method `username' for #<AdminUser:0x00000004bb2e58>
On this code :
Extracted source (around line #7):
4: <% scope = Devise::Mapping.find_scope!(resource_name) %>
5: <%= active_admin_form_for(resource, :as => resource_name, :url => send(:"#{scope}_session_path"), :html => { :id => "session_new" }) do |f|
6: f.inputs do
7: resource.class.authentication_keys.each { |key| f.input key, :input_html => {:autofocus => true}}
8: f.input :password
9: f.input :remember_me, :label => t('active_admin.devise.login.remember_me'), :as => :boolean, :if => false #devise_mapping.rememberable? }
10: end
Anyone a idea how to solve this ?
If you need more info just ask.
Roelof
edit : I thought I could solve this by doing this: http://blog.blazingcloud.net/2012/07/29/activeadmin-with-existing-devise-authentication/
But now all the login are failed ( 401) where they without activeadmin they were successfull
I was having the same error and I solved it adding this to my AdminUser model
def login=(login)
#login = login
end
def login
#login || self.email
end
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(email) = :value", { :value => login.strip.downcase }]).first
end
I'm trying to include a login (username / password) in the header of my application.html.erb. I am getting this error:
Missing partial /login with {:handlers=>[:rjs, :builder, :rhtml, :erb, :rxml], :locale=>[:en, :en], :formats=>[:html]} in view paths "/app/views"
This is happening when I make this call in my application.html.erb:
<%= render '/login' %>
'/login' is defined in my routes.rb as:
match '/login' => "sessions#new", :as => "login"
UPDATE: here is my sessions controller:
class SessionsController < ApplicationController
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
user.last_login = Time.now
user.save
redirect_to root_path, :notice => "login successful"
else
flash.now[:alert] = "invalid login / password combination " # don't show pass + params[:password]
#render :action => "new"
redirect_to login_path, :notice => "wrong user pass"
end
end
def destroy
reset_session
redirect_to root_path, :notice => "successfully logged out"
end
end
I have seen in other posts that this can be due to not defining a variable in a controller action, but since this is a session, and it is in the application.html.erb (application_controller.rb), I'm not sure how to do this. Anybody know how to do this? Thanks!
<%= render "sessions/login", :#user => User.new %>
will render login partial of sessions view, i.e. '_login.html.erb' in views/sessions and instantiate #user to new user so that it can be referenced directly in the partial as :
form_for #user, :url => sessions_path do |f|
f.text_field :email
Check your file extension in my case file extension was rhtml, I changed it into html.erb.
Now its working fine.
Note:
This file with rhtml extension was working fine in rails <= 3.0.10. But stopped working in rails 3.1.12. So I changed its extension as mentioned above.
I cannot simulate my form_tag in Rspec. The form_tag will package the parameter hash as a simple hash while the Rspec will package as a hash of hashes with the parameters, in this case, inside of :session hash like:
{"session"=>{"email"=>"billy#example.com", "password"=>"foobar"}, "controller"=>"sessions", "action"=>"create"}
vs Rails form_tag:
{"utf8"=>"✓", "authenticity_token"=>"AiurjCANkzCJFl+oiJK2tQVzzxrET260bo1wxuDHB74=", "email"=>"billy#example.com", "password"=>"foobar", "commit"=>"Sign in", "action"=>"create", "controller"=>"sessions"}
The Sessions controller is using:
user = User.find_by_email(params[:email])
to extract the email parameter. This will work for the development but for the Rspec test I would have to use:
user = User.find_by_email(params[:session][:email])
Here is my latest Rspec test:
describe "success" do
before(:each) do
#user = Factory(:user)
#attr = {:email => #user.email, :password => #user.password }
end
it "should redirect to the user show page" do
#post :create, :session => #attr #same results
post sessions_path(:email => #user.email, :password => #user.password)
response.should have_selector('h1', :content => #user.full_name)
end
end
This will fail because of the difference in the parameter hashes above. Is there a way in Rspec to send parameters to the session controller in a single hash to match the form_tag as shown above?
You can try to pass the parameters manually in your tests
describe "success" do
before(:each) do
#user = Factory(:user)
#attr = {:email => #user.email, :password => #user.password }
end
it "should redirect to the user show page" do
post :create, :email => #user.email, :password => #user.password
response.should have_selector('h1', :content => #user.full_name)
end
end
This will send the parameters to your method in the controller as
{... "email"=>"billy#example.com", "password"=>"foobar" ...}
Through other posts on SO I've learned that my sign-up process using a nested model form is flawed in that I create a new User, then redirect to create its Profile. Here is the process:
user = User.new
user.email = ...
user.password = ...
user.profile = Profile.new
user.profile.first_name = ...
...
user.profile.save
user.save
It seems as if one solution is to initiate the profile method from within the UsersController create(?) action, so that I POST to both models(?) then redirect to a page with a form to fill out the rest of the profile.
But I'm not entirely sure how to do that, as I am new to programming/Rails. So can anyone give me guidance on how to introduce the Profile method within the UsersController? I gave it a go but don't think it's correct. Code for both Users/ProfilesController below:
User:
def new
#user = User.new
#user.profile = Profile.new
end
def index
#user = User.all
end
def create
#user = User.new(params[:user])
if #user.profile.save
redirect_to profile_new_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Profile:
def new
#user.profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Routes.rb:
match '/signup' => 'profiles#new', :as => "signup"
get "signup" => "profiles#new", :as => "signup"
root :to => 'users#new'
resources :users
resources :profiles
My nested model form (the relevant parts):
<%= form_for(:user, :url => { :action => :create }, :html => {:id => 'homepage'}) do |f| %>
<%= f.text_field :email, :size=> 13, :id => "user[email]" %>
<%= f.fields_for :profile do |f| %>
<% end%>
<% end %>
If anyone could help me I'd greatly appreciate it.
You should have something like this in your models:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
...of course all backed up with proper migrations. Then while building up a form you can use fields_for helper. Here is slightly modified example from docs:
<%= form_for #user do |user_form| %>
Email: <%= user_form.text_field :email %>
<%= user_form.fields_for :profile do |profile_fields| %>
First Name: <%= profile_fields.text_field :first_name %>
<% end %>
<% end %>
And update your user and his profile in the controller in one go, thanks to accepts_nested_attributes_for :profile declaration in your model.
I've been reading over this resource as well as this post to try to understand Routes more (currently learning programming/Rails by doing) but am wondering how I can fix the error I'm getting, which is No route matches {:controller=>"profiles", :action=>"show"}.
I get the error working my way through a Rails 3 sign-up process using nested model forms. The sign-up process, as follows:
user = User.new
user.email = ""
user.password = ""
user.profile = Profile.new
user.profile.save
user.save
The sign-up process starts at the homepage with the following form:
<%= form_for :user, :url => signup_path, :html => {:id => 'homepage'} do |f| %>
<div>
...
</div>
<%= f.fields_for :profile do |f| %>
<% end %>
<% end %>
Then the process goes to fill in the profile, then redirect to the new User's profile after this form is completed:
<%= form_for :profile, :html => { :multipart => true } do |f| %>
<div>
...
</div>
<%= f.fields_for :user do |f| %>
<% end %>
<% end %>
I have accepts_nested_attributes_for :user and :profile in their respective models.
My rails server it gives me a bit more detail:
ActionController::RoutingError (No route matches {:controller=>"profile.save", :action=>"show"}):
app/controllers/profiles_controller.rb:15:in `create'
So in my ProfilesController in 'create':
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, :notice => 'User successfully added.'
else
render :action => 'new'
end
end
Seems clear that the issue is in profile_path, so my Routes.rb:
post "/signup" => "profiles#create", :as => "signup"
match "skip/signup", :to => "info#signupskip"
match "skip/profiles/new", :to => "profiles#newskip"
root :to => "users#create"
Can anyone help shed light on what I'm doing wrong/missing in my Routes.rb file?
The redirect path should contain the specific profile to redirect to:
if #profile.save
redirect_to profile_path(#profile), :notice => 'User successfully added.'
else
.....
Also the routes should include this line:
get "/profiles/:id" => "profiles#show", as => "profile"