This changeset adds real "remember me" functionality. The checkbox on the login page "Stay logged in" previously prevented an inactive session from expiring. Now, it also functions to remember that a user is logged in across browser sessions (i.e. a user exits the browser, and reopens it).

I've also ensured that all tests (including selenium tests) are passing on my machine.

This changeset should be back to stable and usable.



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@561 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2007-07-08 06:41:10 +00:00
parent 855f6e0beb
commit 16b9c2947b
17 changed files with 285 additions and 243 deletions

View file

@ -21,6 +21,8 @@ class ApplicationController < ActionController::Base
prepend_before_filter :enable_mobile_content_negotiation
after_filter :restore_content_type_for_mobile
after_filter :set_charset
include ActionView::Helpers::TextHelper
helper_method :format_date, :markdown

View file

@ -4,6 +4,7 @@ class LoginController < ApplicationController
filter_parameter_logging :user_password
skip_before_filter :set_session_expiration
skip_before_filter :login_required
before_filter :login_optional
before_filter :get_current_user
open_id_consumer if Tracks::Config.openid_enabled?
@ -15,11 +16,15 @@ class LoginController < ApplicationController
if @user = User.authenticate(params['user_login'], params['user_password'])
session['user_id'] = @user.id
# If checkbox on login page checked, we don't expire the session after 1 hour
# of inactivity
# of inactivity and we remember this user for future browser sessions
session['noexpiry'] = params['user_noexpiry']
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
notify :notice, "Login successful: session #{msg}"
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year }
unless should_expire_sessions?
@user.remember_me
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at }
end
redirect_back_or_home
return
else
@ -90,6 +95,10 @@ class LoginController < ApplicationController
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
notify :notice, "You have successfully verified #{openid_url} as your identity. Login successful: session #{msg}"
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year }
unless should_expire_sessions?
@user.remember_me
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at }
end
cookies[:openid_url] = { :value => openid_url, :expires => Time.now + 1.year }
redirect_back_or_home
else
@ -106,6 +115,8 @@ class LoginController < ApplicationController
end
def logout
@user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
notify :notice, "You have been logged out of Tracks."

View file

@ -1,6 +1,9 @@
require 'digest/sha1'
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
attr_accessor :password
has_many :contexts,
:order => 'position ASC',
:dependent => :delete_all do
@ -89,6 +92,9 @@ class User < ActiveRecord::Base
validates_uniqueness_of :login, :on => :create
validates_presence_of :open_id_url, :if => Proc.new{|user| user.auth_type == 'open_id'}
before_create :crypt_password, :crypt_word
before_update :crypt_password
def validate
unless Tracks::Config.auth_schemes.include?(auth_type)
errors.add("auth_type", "not a valid authentication type")
@ -101,7 +107,7 @@ class User < ActiveRecord::Base
candidate = find(:first, :conditions => ["login = ?", login])
return nil if candidate.nil?
if candidate.auth_type == 'database'
return candidate if candidate.password == sha1(pass)
return candidate if candidate.crypted_password == sha1(pass)
elsif candidate.auth_type == 'ldap' && Tracks::Config.auth_schemes.include?('ldap')
return candidate if SimpleLdapAuthenticator.valid?(login, pass)
end
@ -136,10 +142,6 @@ class User < ActiveRecord::Base
self.password_confirmation = pass_confirm
save!
end
def crypt_word
write_attribute("word", self.class.sha1(login + Time.now.to_i.to_s + rand.to_s))
end
def time
prefs.tz.adjust(Time.now.utc)
@ -148,22 +150,45 @@ class User < ActiveRecord::Base
def date
time.to_date
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
self.remember_token_expires_at = 2.weeks.from_now.utc
self.remember_token = self.class.sha1("#{login}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
protected
def self.sha1(pass)
Digest::SHA1.hexdigest("#{Tracks::Config.salt}--#{pass}--")
end
before_create :crypt_password, :crypt_word
before_update :crypt_password
def crypt_word
write_attribute("word", self.class.sha1(login + Time.now.to_i.to_s + rand.to_s))
end
def crypt_password
write_attribute("password", self.class.sha1(password)) if password == @password_confirmation
return if password.blank?
write_attribute("crypted_password", self.class.sha1(password)) if password == password_confirmation
end
def password_required?
auth_type == 'database'
auth_type == 'database' && crypted_password.blank? || !password.blank?
end
def password_matches?(pass)
crypted_password == sha1(pass)
end
end

View file

@ -1,211 +1,140 @@
# This file is autogenerated. Instead of editing this file, please use the
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
ActiveRecord::Schema.define(:version => 33) do
create_table "bow_wows", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "cats", :force => true do |t|
t.column "name", :string
t.column "cat_type", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "contexts", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "hide", :integer, :limit => 4, :default => 0, :null => false
t.column "position", :integer, :default => 0, :null => false
t.column "user_id", :integer, :default => 0, :null => false
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id"
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
create_table "eaters_foodstuffs", :force => true do |t|
t.column "foodstuff_id", :integer
t.column "eater_id", :integer
t.column "some_attribute", :integer, :default => 0
t.column "eater_type", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "fish", :force => true do |t|
t.column "name", :string
t.column "speed", :integer
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "frogs", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "keep_your_enemies_close", :force => true do |t|
t.column "enemy_id", :integer
t.column "enemy_type", :string
t.column "protector_id", :integer
t.column "protector_type", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "little_whale_pupils", :force => true do |t|
t.column "whale_id", :integer
t.column "aquatic_pupil_id", :integer
t.column "aquatic_pupil_type", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "notes", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
t.column "project_id", :integer, :default => 0, :null => false
t.column "body", :text
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
create_table "open_id_associations", :force => true do |t|
t.column "server_url", :binary
t.column "handle", :string
t.column "secret", :binary
t.column "issued", :integer
t.column "lifetime", :integer
t.column "assoc_type", :string
end
create_table "open_id_nonces", :force => true do |t|
t.column "nonce", :string
t.column "created", :integer
end
create_table "open_id_settings", :force => true do |t|
t.column "setting", :string
t.column "value", :binary
end
create_table "petfoods", :id => false, :force => true do |t|
t.column "the_petfood_primary_key", :integer, :null => false
t.column "name", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "preferences", :force => true do |t|
t.column "user_id", :integer, :default => 0, :null => false
t.column "date_format", :string, :limit => 40, :default => "%d/%m/%Y", :null => false
t.column "week_starts", :integer, :default => 0, :null => false
t.column "show_number_completed", :integer, :default => 5, :null => false
t.column "staleness_starts", :integer, :default => 7, :null => false
t.column "show_completed_projects_in_sidebar", :boolean, :default => true, :null => false
t.column "show_hidden_contexts_in_sidebar", :boolean, :default => true, :null => false
t.column "due_style", :integer, :default => 0, :null => false
t.column "admin_email", :string, :default => "butshesagirl@rousette.org.uk", :null => false
t.column "refresh", :integer, :default => 0, :null => false
t.column "verbose_action_descriptors", :boolean, :default => false, :null => false
t.column "show_hidden_projects_in_sidebar", :boolean, :default => true, :null => false
t.column "time_zone", :string, :default => "London", :null => false
t.column "show_project_on_todo_done", :boolean, :default => false, :null => false
t.column "title_date_format", :string, :default => "%A, %d %B %Y", :null => false
t.column "mobile_todos_per_page", :integer, :default => 6, :null => false
end
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
create_table "projects", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "position", :integer, :default => 0, :null => false
t.column "user_id", :integer, :default => 0, :null => false
t.column "description", :text
t.column "state", :string, :limit => 20, :default => "active", :null => false
t.column "created_at", :datetime
t.column "updated_at", :datetime
t.column "default_context_id", :integer
end
add_index "projects", ["user_id"], :name => "index_projects_on_user_id"
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
create_table "sessions", :force => true do |t|
t.column "session_id", :string
t.column "data", :text
t.column "updated_at", :datetime
end
add_index "sessions", ["session_id"], :name => "sessions_session_id_index"
create_table "taggings", :force => true do |t|
t.column "taggable_id", :integer
t.column "tag_id", :integer
t.column "taggable_type", :string
t.column "user_id", :integer
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
create_table "tags", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
add_index "tags", ["name"], :name => "index_tags_on_name"
create_table "todos", :force => true do |t|
t.column "context_id", :integer, :default => 0, :null => false
t.column "description", :string, :limit => 100, :default => "", :null => false
t.column "notes", :text
t.column "created_at", :datetime
t.column "due", :date
t.column "completed_at", :datetime
t.column "project_id", :integer
t.column "user_id", :integer, :default => 0, :null => false
t.column "show_from", :date
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
end
add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state"
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
create_table "users", :force => true do |t|
t.column "login", :string, :limit => 80
t.column "crypted_password", :string, :limit => 40
t.column "word", :string
t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false
t.column "first_name", :string
t.column "last_name", :string
t.column "auth_type", :string, :default => "database", :null => false
t.column "open_id_url", :string
t.column "remember_token", :string
t.column "remember_token_expires_at", :datetime
end
add_index "users", ["login"], :name => "index_users_on_login"
create_table "whales", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
create_table "wild_boars", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime, :null => false
t.column "updated_at", :datetime, :null => false
end
end
# This file is autogenerated. Instead of editing this file, please use the
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
ActiveRecord::Schema.define(:version => 33) do
create_table "contexts", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "position", :integer, :null => false
t.column "hide", :boolean, :default => false
t.column "user_id", :integer, :default => 1
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id"
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
create_table "notes", :force => true do |t|
t.column "user_id", :integer, :null => false
t.column "project_id", :integer, :null => false
t.column "body", :text
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
create_table "open_id_associations", :force => true do |t|
t.column "server_url", :binary
t.column "handle", :string
t.column "secret", :binary
t.column "issued", :integer
t.column "lifetime", :integer
t.column "assoc_type", :string
end
create_table "open_id_nonces", :force => true do |t|
t.column "nonce", :string
t.column "created", :integer
end
create_table "open_id_settings", :force => true do |t|
t.column "setting", :string
t.column "value", :binary
end
create_table "preferences", :force => true do |t|
t.column "user_id", :integer, :null => false
t.column "date_format", :string, :limit => 40, :default => "%d/%m/%Y", :null => false
t.column "week_starts", :integer, :default => 0, :null => false
t.column "show_number_completed", :integer, :default => 5, :null => false
t.column "staleness_starts", :integer, :default => 7, :null => false
t.column "show_completed_projects_in_sidebar", :boolean, :default => true, :null => false
t.column "show_hidden_contexts_in_sidebar", :boolean, :default => true, :null => false
t.column "due_style", :integer, :default => 0, :null => false
t.column "admin_email", :string, :default => "butshesagirl@rousette.org.uk", :null => false
t.column "refresh", :integer, :default => 0, :null => false
t.column "verbose_action_descriptors", :boolean, :default => false, :null => false
t.column "show_hidden_projects_in_sidebar", :boolean, :default => true, :null => false
t.column "time_zone", :string, :default => "London", :null => false
t.column "show_project_on_todo_done", :boolean, :default => false, :null => false
t.column "title_date_format", :string, :default => "%A, %d %B %Y", :null => false
t.column "mobile_todos_per_page", :integer, :default => 6, :null => false
end
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
create_table "projects", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "position", :integer, :null => false
t.column "user_id", :integer, :default => 1
t.column "description", :text
t.column "state", :string, :limit => 20, :default => "active", :null => false
t.column "created_at", :datetime
t.column "updated_at", :datetime
t.column "default_context_id", :integer
end
add_index "projects", ["user_id"], :name => "index_projects_on_user_id"
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
create_table "sessions", :force => true do |t|
t.column "session_id", :string
t.column "data", :text
t.column "updated_at", :datetime
end
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
create_table "taggings", :force => true do |t|
t.column "taggable_id", :integer
t.column "tag_id", :integer
t.column "taggable_type", :string
t.column "user_id", :integer
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
create_table "tags", :force => true do |t|
t.column "name", :string
t.column "created_at", :datetime
t.column "updated_at", :datetime
end
add_index "tags", ["name"], :name => "index_tags_on_name"
create_table "todos", :force => true do |t|
t.column "context_id", :integer, :null => false
t.column "project_id", :integer
t.column "description", :string, :default => "", :null => false
t.column "notes", :text
t.column "created_at", :datetime
t.column "due", :date
t.column "completed_at", :datetime
t.column "user_id", :integer, :default => 1
t.column "show_from", :date
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
end
add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state"
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
create_table "users", :force => true do |t|
t.column "login", :string, :limit => 80, :default => "", :null => false
t.column "crypted_password", :string, :limit => 40
t.column "word", :string
t.column "is_admin", :boolean, :default => false, :null => false
t.column "first_name", :string
t.column "last_name", :string
t.column "auth_type", :string, :default => "database", :null => false
t.column "open_id_url", :string
t.column "remember_token", :string
t.column "remember_token_expires_at", :datetime
end
add_index "users", ["login"], :name => "index_users_on_login"
end

View file

@ -1,7 +1,7 @@
module AuthenticatedTestHelper
# Sets the current user in the session from the user fixtures.
def login_as(user)
@request.session[:user] = user ? users(user).id : nil
@request.session['user_id'] = user ? users(user).id : nil
end
def content_type(type)

View file

@ -31,6 +31,20 @@ module LoginSystem
true
end
# When called with before_filter :login_from_cookie will check for an :auth_token
# cookie and log the user back in if appropriate
def login_from_cookie
return unless cookies[:auth_token] && !logged_in?
user = User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
session['user_id'] = user.id
@user = user
@user.remember_me
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at }
flash[:notice] = "Logged in successfully. Welcome back!"
end
end
def login_or_feed_token_required
if ['rss', 'atom', 'txt', 'ics'].include?(params[:format])
if user = User.find_by_word(params[:token])
@ -55,6 +69,8 @@ module LoginSystem
if not protect?(action_name)
return true
end
login_from_cookie
if session['user_id'] and authorize?(get_current_user)
return true
@ -78,6 +94,8 @@ module LoginSystem
def login_optional
login_from_cookie
if session['user_id'] and authorize?(get_current_user)
return true
end
@ -92,6 +110,11 @@ module LoginSystem
return true
end
def logged_in?
get_current_user
@user != nil
end
def get_current_user
if @user.nil? && session['user_id']
@user = User.find session['user_id'], :include => :preference
@ -164,6 +187,6 @@ module LoginSystem
response.headers["Status"] = "Unauthorized"
response.headers["WWW-Authenticate"] = "Basic realm=\"'Tracks Login Required'\""
render :text => "401 Unauthorized: You are not authorized to interact with Tracks.", :status => 401
end
end
end

View file

@ -2,7 +2,7 @@
admin_user:
id: 1
login: admin
password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--") %>
crypted_password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--") %>
word: <%= Digest::SHA1.hexdigest("adminSat Feb 25 17:14:00 GMT 20060.236961325863376") %>
is_admin: true
first_name: Admin
@ -12,7 +12,7 @@ admin_user:
other_user:
id: 2
login: jane
password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--") %>
crypted_password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--") %>
word: <%= Digest::SHA1.hexdigest("janeSun Feb 19 14:42:45 GMT 20060.408173979260027") %>
is_admin: false
first_name: Jane
@ -22,7 +22,7 @@ other_user:
ldap_user:
id: 3
login: john
password: test
crypted_password: test
word: <%= Digest::SHA1.hexdigest("johnSun Feb 19 14:42:45 GMT 20060.408173979260027") %>
is_admin: false
first_name: John

View file

@ -76,6 +76,44 @@ class LoginControllerTest < Test::Rails::TestCase
assert_response :success
end
def test_should_remember_me
post :login, :user_login => 'jane', :user_password => 'sesame', :user_noexpiry => "on"
assert_not_nil @response.cookies["auth_token"]
end
def test_should_not_remember_me
post :login, :user_login => 'jane', :user_password => 'sesame', :user_noexpiry => "off"
assert_nil @response.cookies["auth_token"]
end
def test_should_delete_token_on_logout
login_as :other_user
get :logout
assert_equal @response.cookies["auth_token"], []
end
def test_should_login_with_cookie
users(:other_user).remember_me
@request.cookies["auth_token"] = cookie_for(:other_user)
get :login
assert @controller.send(:logged_in?)
end
def test_should_fail_expired_cookie_login
users(:other_user).remember_me
users(:other_user).update_attribute :remember_token_expires_at, 5.minutes.ago
@request.cookies["auth_token"] = cookie_for(:other_user)
get :login
assert !@controller.send(:logged_in?)
end
def test_should_fail_cookie_login
users(:other_user).remember_me
@request.cookies["auth_token"] = auth_token('invalid_auth_token')
get :login
assert !@controller.send(:logged_in?)
end
private
# Logs in a user and returns the user object found in the session object
@ -85,5 +123,13 @@ class LoginControllerTest < Test::Rails::TestCase
return User.find(session['user_id'])
end
def auth_token(token)
CGI::Cookie.new('name' => 'auth_token', 'value' => token)
end
def cookie_for(user)
auth_token users(user).remember_token
end
end

View file

@ -6,7 +6,7 @@ require 'preference'
class PreferencesController; def rescue_action(e) raise e end; end
class PreferencesControllerTest < Test::Rails::TestCase
fixtures :users
fixtures :users, :preferences
def setup
assert_equal "test", ENV['RAILS_ENV']
@ -19,7 +19,7 @@ class PreferencesControllerTest < Test::Rails::TestCase
def test_preferences
get :index # should fail because no login
assert_redirected_to :controller => 'login', :action => 'login'
@request.session['user_id'] = users(:admin_user).id # log in the admin user
login_as :admin_user
get :index
assert_response :success
assert_equal assigns['page_title'], "TRACKS::Preferences"
@ -29,7 +29,7 @@ class PreferencesControllerTest < Test::Rails::TestCase
def test_edit_preferences
get :edit # should fail because no login
assert_redirected_to :controller => 'login', :action => 'login'
@request.session['user_id'] = users(:admin_user).id # log in the admin user
login_as :admin_user
get :edit
assert_response :success
assert_equal assigns['page_title'], "TRACKS::Edit Preferences"
@ -40,7 +40,7 @@ class PreferencesControllerTest < Test::Rails::TestCase
def test_update_preferences
login_as :admin_user
post :update, {:user => { :first_name => 'Jane', :last_name => 'Doe'}, :prefs => { :date_format => "%m-%d-%Y", :week_starts => "0", :show_number_completed => "10", :show_completed_projects_in_sidebar => "false", :show_hidden_contexts_in_sidebar => "false", :staleness_starts => "14", :due_style => "1", :admin_email => "my.email@domain.com" }}
updated_admin_user = User.find(users(:admin_user).id)
updated_admin_user = users(:admin_user).reload
assert_not_nil updated_admin_user.preference
assert_equal 'Jane', updated_admin_user.first_name
assert_equal 'Doe', updated_admin_user.last_name

View file

@ -57,7 +57,7 @@ class UsersControllerTest < Test::Rails::TestCase
post :update_password, :updateuser => {:password => 'newpassword', :password_confirmation => 'newpassword'}
assert_redirected_to :controller => 'preferences'
@updated_user = User.find(users(:admin_user).id)
assert_equal @updated_user.password, Digest::SHA1.hexdigest("#{Tracks::Config.salt}--newpassword--")
assert_equal @updated_user.crypted_password, Digest::SHA1.hexdigest("#{Tracks::Config.salt}--newpassword--")
assert_equal "Password updated.", flash[:notice]
end

View file

@ -1,5 +1,5 @@
type "todo_description", "choose era"
type "todo_show_from", "1/1/2030"
click "//input[@value='Add action']"
click "css=#todo-form-new-action .submit_box button"
wait_for_element_present "xpath=//div[@id='tickler'] //div[@class='item-container']"
wait_for_element_present "xpath=//div[@id='tickler'] //div[@class='item-container'] //a[@title='01/01/2030']"

View file

@ -6,7 +6,7 @@ open "/projects/1"
click "edit_icon_todo_5"
wait_for_element_present "show_from_todo_5"
type "show_from_todo_5", "1/1/2030"
click "//input[@value='Update']"
click "css=#submit_todo_5"
wait_for_element_present "xpath=//div[@id='tickler'] //div[@id='todo_5']"
#now activate the other deferred one
@ -14,7 +14,7 @@ open "/projects/1"
click "edit_icon_todo_15"
wait_for_element_present "show_from_todo_15"
type "show_from_todo_15", ""
click "//input[@value='Update']"
click "css=#submit_todo_15"
wait_for_element_present "xpath=//div[@id='p1'] //div[@id='todo_15']"
assert_not_visible "tickler-empty-nd"
assert_text 'badge_count', '2'

View file

@ -4,6 +4,6 @@ open "/projects/1"
click "edit_icon_todo_15"
wait_for_element_present "show_from_todo_15"
type "show_from_todo_15", ""
click "//input[@value='Update']"
click "css=#submit_todo_15"
wait_for_element_present "xpath=//div[@id='p1'] //div[@id='todo_15']"
wait_for_visible "tickler-empty-nd"

View file

@ -4,6 +4,6 @@ open "/projects/1"
click "edit_icon_todo_5"
wait_for_element_present "show_from_todo_5"
type "project_name_todo_5", ""
click "//input[@value='Update']"
click "css=#submit_todo_5"
wait_for_element_not_present "todo_5"
assert_text 'badge_count', '1'

View file

@ -4,7 +4,7 @@ open "/projects/1"
click "edit_icon_todo_5"
wait_for_element_present "show_from_todo_5"
type "show_from_todo_5", "1/1/2030"
click "//input[@value='Update']"
click "css=#submit_todo_5"
wait_for_element_present "xpath=//div[@id='tickler'] //div[@id='todo_5']"
assert_not_visible "tickler-empty-nd"
assert_text 'badge_count', '1'

View file

@ -31,6 +31,12 @@ class Test::Unit::TestCase
end
alias_method :assert_errors_on, :assert_error_on
def assert_value_changed(object, method = nil)
initial_value = object.send(method)
yield
assert_not_equal initial_value, object.send(method), "#{object}##{method}"
end
end

View file

@ -33,7 +33,7 @@ class UserTest < Test::Rails::TestCase
assert_kind_of User, @admin_user
assert_equal 1, @admin_user.id
assert_equal "admin", @admin_user.login
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--")}", @admin_user.password
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--abracadabra--")}", @admin_user.crypted_password
assert_not_nil @admin_user.word
assert @admin_user.is_admin
end
@ -43,7 +43,7 @@ class UserTest < Test::Rails::TestCase
assert_kind_of User, @other_user
assert_equal 2, @other_user.id
assert_equal "jane", @other_user.login
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.crypted_password
assert_not_nil @other_user.word
assert @other_user.is_admin == false || @other_user.is_admin == 0
end
@ -179,9 +179,9 @@ class UserTest < Test::Rails::TestCase
end
def test_crypt_word_updates_word
old_word = @admin_user.word
@admin_user.crypt_word
assert_not_equal old_word, @admin_user.word
assert_value_changed @admin_user, :word do
@admin_user.send :crypt_word
end
end
def test_find_admin