Added browser tests for password reset.

This commit is contained in:
Martin Edenhofer 2014-12-31 10:04:14 +01:00
parent 67b32a0bca
commit 0d9adc0d64
12 changed files with 324 additions and 100 deletions

View file

@ -44,120 +44,156 @@ class Index extends App.ControllerContent
# get data # get data
@ajax( @ajax(
id: 'password_reset' id: 'password_reset'
type: 'POST' type: 'POST'
url: @apiPath + '/users/password_reset' url: @apiPath + '/users/password_reset'
data: JSON.stringify(params) data: JSON.stringify(params)
processData: true processData: true
success: @success success: @success
error: @error
) )
success: (data, status, xhr) => success: (data) =>
@render( sent: true ) if data.message is 'ok'
error: (data, status, xhr) => # if in developer mode, redirect to set new password
@notify( if data.token && @Config.get('developer_mode') is true
type: 'error' redirect = =>
msg: App.i18n.translateContent( 'Username or email address invalid, please try again.' ) @navigate "#password_reset_verify/#{data.token}"
) @delay( redirect, 2000 )
@formEnable( @el.find('.form-password') ) @render( sent: true )
else
@$('[name=username]').val('')
@notify(
type: 'error'
msg: App.i18n.translateContent( 'Username or email address invalid, please try again.' )
)
@formEnable( @el.find('.form-password') )
App.Config.set( 'reset_password', Index, 'Routes' ) App.Config.set( 'reset_password', Index, 'Routes' )
class Verify extends App.ControllerContent class Verify extends App.ControllerContent
events: events:
'submit form': 'submit' 'submit form': 'submit'
'click .submit': 'submit' 'click .submit': 'submit'
constructor: -> constructor: ->
super super
@navHide()
# set title # set title
@title 'Reset Password' @title 'Reset Password'
@navupdate '#reset_password_verify' @navupdate '#reset_password_verify'
# get data # get data
params = {} params =
params['token'] = @token token: @token
@ajax( @ajax(
id: 'password_reset_verify' id: 'password_reset_verify'
type: 'POST' type: 'POST'
url: @apiPath + '/users/password_reset_verify' url: @apiPath + '/users/password_reset_verify'
data: JSON.stringify(params) data: JSON.stringify(params)
processData: true processData: true
success: @render_success success: @render_change
error: @render_failed
) )
render_success: => render_change: (data) =>
configure_attributes = [ if data.message is 'ok'
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input span4', }, configure_attributes = [
] { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', },
]
@html App.view('password/reset_change')() @html App.view('password/reset_change')()
new App.ControllerForm( new App.ControllerForm(
el: @el.find('#form-password-change') el: @el.find('.form-password-change')
model: { configure_attributes: configure_attributes } model: { configure_attributes: configure_attributes }
autofocus: true autofocus: true
) )
else
render_failed: => @html App.view('password/reset_failed')(
@html App.view('generic/hero_message')( head: 'Reset Password failed!'
head: 'Failed!' message: 'Token is invalid!'
message: 'Token is not valid!' )
)
submit: (e) -> submit: (e) ->
e.preventDefault() e.preventDefault()
params = @formParam(e.target) params = @formParam(e.target)
params['token'] = @token params['token'] = @token
@password = params['password'] @password = params['password']
# disable form
@formDisable(e)
# validate
if params['password_confirm'] isnt params['password']
@formEnable(e)
@$('[name=password]').val('')
@$('[name=password_confirm]').val('')
@notify
type: 'error'
msg: 'Can\'t update password, your new passwords do not match. Please try again!'
removeAll: true
return
if !params['password']
@formEnable(e)
@notify
type: 'error'
msg: 'Please supply your new password!'
removeAll: true
return
# get data # get data
@ajax( @ajax(
id: 'password_reset_verify' id: 'password_reset_verify'
type: 'POST' type: 'POST'
url: @apiPath + '/users/password_reset_verify' url: @apiPath + '/users/password_reset_verify'
data: JSON.stringify(params) data: JSON.stringify(params)
processData: true processData: true
success: @render_changed_success success: @render_changed
error: @render_changed_failed
) )
render_changed_success: (data, status, xhr) => render_changed: (data, status, xhr) =>
App.Auth.login( if data.message is 'ok'
data: App.Auth.login(
username: data.user_login data:
password: @password username: data.user_login
success: => password: @password
success: =>
# login check # login check
App.Auth.loginCheck() App.Auth.loginCheck()
# add notify # add notify
@notify @notify
type: 'success' type: 'success'
msg: 'Woo hoo! Your password has been changed!' msg: 'Woo hoo! Your password has been changed!'
removeAll: true removeAll: true
# redirect to # # redirect to #
@navigate '#' @navigate '#'
error: => error: =>
@formEnable( @$('form') )
# add notify # add notify
@notify
type: 'error'
msg: 'Something went wrong. Please contact your administrator.'
removeAll: true
)
else
if data.notice
@notify @notify
type: 'error' type: 'error'
msg: 'Something went wrong. Please contact your administrator.' msg: App.i18n.translateContent( data.notice[0], data.notice[1] )
removeAll: true removeAll: true
) else
@notify
type: 'error'
msg: 'Unable to set password. Please contact your administrator.'
removeAll: true
@formEnable( @$('form') )
render_changed_failed: => App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' )
@html App.view('generic/hero_message')(
head: 'Failed!'
message: 'Ask your admin!'
)
App.Config.set( 'password_reset_verify/:token', Verify, 'Routes' )

View file

@ -1,8 +0,0 @@
<div class="hero-unit">
<h2><%- @T( @head ) %> <small><%- @T( @head_small ) %></small></h2>
<div class="container">
<p>
<%- @message %>
</p>
</div>
</div>

View file

@ -1,8 +1,12 @@
<div class="fullHeight vertical center justified reset_password fit"> <div class="fullHeight vertical center justified reset_password fit">
<div class="hero-unit"> <div class="hero-unit">
<h2><%- @T( 'Choose your new password.' ) %><small></small></h2> <h2><%- @T( 'Choose your new password.' ) %><small></small></h2>
<form id="form-password-change"> <form>
<button class="btn btn--primary submit"><%- @T( 'Submit' ) %></button> <div class="form-password-change"></div>
<div class="form-controls">
<a class="subtle-link standalone pull-left cancel" href="#/"><%- @T( 'Cancel & Go Back' ) %></a>
<button class="btn btn--primary submit pull-right"><%- @T( 'Submit' ) %></button>
</div>
</form> </form>
</div> </div>
</div> </div>

View file

@ -0,0 +1,9 @@
<div class="reset_password fullscreen">
<div class="fullscreen-center">
<div class="hero-unit fullscreen-body">
<h2><%- @T( @head ) %><small></small></h2>
<p><%- @message %></p>
<a href="#reset_password" class="subtle retry">&raquo; <%- @T('try again') %> &laquo;</a>
</div>
</div>
</div>

View file

@ -367,12 +367,22 @@ curl http://localhost/api/v1/users/password_reset.json -v -u #{login}:#{password
return return
end end
success = User.password_reset_send( params[:username] ) token = User.password_reset_send( params[:username] )
if success if token
# only if system is in develop mode, send token back to browser for browser tests
if Setting.get('developer_mode') == true
render :json => { :message => 'ok', :token => token.name }, :status => :ok
return
end
# token sent to user, send ok to browser
render :json => { :message => 'ok' }, :status => :ok render :json => { :message => 'ok' }, :status => :ok
else return
render :json => { :message => 'failed' }, :status => :unprocessable_entity
end end
# unable to generate token
render :json => { :message => 'failed' }, :status => :ok
end end
=begin =begin
@ -443,12 +453,12 @@ curl http://localhost/api/v1/users/password_change.json -v -u #{login}:#{passwor
# check old password # check old password
if !params[:password_old] if !params[:password_old]
render :json => { :message => 'failed', :notice => ['Old password needed!'] }, :status => :ok render :json => { :message => 'failed', :notice => ['Current password needed!'] }, :status => :ok
return return
end end
user = User.authenticate( current_user.login, params[:password_old] ) user = User.authenticate( current_user.login, params[:password_old] )
if !user if !user
render :json => { :message => 'failed', :notice => ['Old password is wrong!'] }, :status => :ok render :json => { :message => 'failed', :notice => ['Current password is wrong!'] }, :status => :ok
return return
end end

View file

@ -267,7 +267,7 @@ send reset password email with token to user
returns returns
result = true|false result = token
=end =end
@ -325,7 +325,7 @@ returns
:subject => data[:subject], :subject => data[:subject],
:body => data[:body] :body => data[:body]
) )
true token
end end
=begin =begin

View file

@ -0,0 +1,16 @@
class AddDevelopMode < ActiveRecord::Migration
def up
Setting.create_if_not_exists(
:title => 'Develop System',
:name => 'developer_mode',
:area => 'Core::Develop',
:description => 'Defines if application is in developer mode (useful for developer, all users have the same password, password reset will work without email delivery).',
:options => {},
:state => false,
:frontend => true
)
end
def down
end
end

View file

@ -15,6 +15,15 @@ Setting.create_if_not_exists(
:state => false, :state => false,
:frontend => true :frontend => true
) )
Setting.create_if_not_exists(
:title => 'Developer System',
:name => 'developer_mode',
:area => 'Core::Develop',
:description => 'Defines if application is in developer mode (useful for developer, all users have the same password, password reset will work without email delivery).',
:options => {},
:state => false,
:frontend => true
)
Setting.create_if_not_exists( Setting.create_if_not_exists(
:title => 'Online Service', :title => 'Online Service',
:name => 'system_online_service', :name => 'system_online_service',

View file

@ -23,7 +23,7 @@ returns
:adapter => 'Auth::Internal', :adapter => 'Auth::Internal',
}, },
{ {
:adapter => 'Auth::Test', :adapter => 'Auth::Developer',
}, },
] ]

View file

@ -1,10 +1,10 @@
# Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2013 Zammad Foundation, http://zammad-foundation.org/
module Auth::Test module Auth::Developer
def self.check( username, password, config, user ) def self.check( username, password, config, user )
# development systems # development systems
if !ENV['RAILS_ENV'] || ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test' if Setting.get('developer_mode') == true
return user if password == 'test' return user if password == 'test'
end end

View file

@ -28,8 +28,8 @@ rake db:seed
# modify production.rb to serve assets # modify production.rb to serve assets
cat config/environments/production.rb | sed -e 's/config.serve_static_assets = false/config.serve_static_assets = true/' > /tmp/production.rb && cp /tmp/production.rb config/environments/production.rb cat config/environments/production.rb | sed -e 's/config.serve_static_assets = false/config.serve_static_assets = true/' > /tmp/production.rb && cp /tmp/production.rb config/environments/production.rb
# mofidy auth backend # set system to develop mode
cat lib/auth/test.rb | sed "s/\] == 'test'/] == 'production'/" > /tmp/test.rb && cp /tmp/test.rb lib/auth/test.rb rails r "Setting.set('developer_mode', true)"
pumactl --pidfile tmp/pids/puma.pid stop pumactl --pidfile tmp/pids/puma.pid stop
script/websocket-server.rb stop script/websocket-server.rb stop
@ -42,7 +42,7 @@ sleep 15
#export REMOTE_URL='http://medenhofer:765d0dd4-994b-4e15-9f89-13f3aedeb462@ondemand.saucelabs.com:80/wd/hub' BROWSER_OS='Windows 2012' BROWSER_VERSION=20 BROWSER=firefox #export REMOTE_URL='http://medenhofer:765d0dd4-994b-4e15-9f89-13f3aedeb462@ondemand.saucelabs.com:80/wd/hub' BROWSER_OS='Windows 2012' BROWSER_VERSION=20 BROWSER=firefox
rake test:browser["BROWSER_URL=http://localhost:4444"] rake test:browser["BROWSER_URL=http://localhost:4444"]
#rake test:browser["BROWSER_URL=http://192.168.178.20:4444"] #rake test:browser["BROWSER_URL=http://192.168.178.28:4444"]
script/websocket-server.rb stop script/websocket-server.rb stop

View file

@ -1,7 +1,7 @@
# encoding: utf-8 # encoding: utf-8
require 'browser_test_helper' require 'browser_test_helper'
class SignupTest < TestCase class SignupPasswordChangeAndResetTest < TestCase
def test_signup def test_signup
signup_user_email = 'signup-test-' + rand(999999).to_s + '@example.com' signup_user_email = 'signup-test-' + rand(999999).to_s + '@example.com'
tests = [ tests = [
@ -112,7 +112,7 @@ class SignupTest < TestCase
{ {
:execute => 'watch_for', :execute => 'watch_for',
:area => 'body', :area => 'body',
:value => 'old password is wrong', :value => 'current password is wrong',
}, },
{ {
:execute => 'set', :execute => 'set',
@ -198,6 +198,154 @@ class SignupTest < TestCase
:username => signup_user_email, :username => signup_user_email,
:password => 'some-pass-new2', :password => 'some-pass-new2',
}, },
{
:execute => 'logout',
},
],
},
{
:name => 'reset password',
:action => [
# got to wrong url
{
:execute => 'navigate',
:to => browser_url + '/#password_reset_verify/not_existing_token',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'Token is invalid',
},
# correct way
{
:execute => 'click',
:css => 'a[href="#reset_password"]',
},
{
:execute => 'set',
:css => 'input[name="username"]',
:value => 'nonexisiting',
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'address invalid',
},
{
:execute => 'set',
:css => 'input[name="username"]',
:value => signup_user_email,
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'sent password reset instructions',
},
# redirect to "#password_reset_verify/#{token}" url by app, because of "developer_mode"
{
:execute => 'watch_for',
:area => 'body',
:value => 'Choose your new password',
},
# set new password
{
:execute => 'set',
:css => 'input[name="password"]',
:value => 'some',
},
{
:execute => 'set',
:css => 'input[name="password_confirm"]',
:value => 'some2',
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'passwords do not match',
},
{
:execute => 'set',
:css => 'input[name="password"]',
:value => 'some',
},
{
:execute => 'set',
:css => 'input[name="password_confirm"]',
:value => 'some',
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'it must be at least',
},
{
:execute => 'set',
:css => 'input[name="password"]',
:value => 'some-pass-new',
},
{
:execute => 'set',
:css => 'input[name="password_confirm"]',
:value => 'some-pass-new',
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'must contain at least 1 digit',
},
{
:execute => 'set',
:css => 'input[name="password"]',
:value => 'some-pass-new2',
},
{
:execute => 'set',
:css => 'input[name="password_confirm"]',
:value => 'some-pass-new2',
},
{
:execute => 'click',
:css => '.content .btn--primary',
},
{
:execute => 'watch_for',
:area => 'body',
:value => 'Your password has been changed',
},
{
:execute => 'wait',
:value => 5,
},
{
:execute => 'match',
:css => '.user-menu .user a',
:attribute => 'title',
:value => signup_user_email,
:match_result => true,
},
], ],
}, },
] ]