devise_invitable 1.1.8 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/CHANGELOG +5 -0
  2. data/README.rdoc +39 -12
  3. data/app/controllers/devise/invitations_controller.rb +20 -6
  4. data/app/controllers/devise_invitable/registrations_controller.rb +2 -2
  5. data/app/views/devise/mailer/invitation_instructions.html.erb +1 -1
  6. data/lib/devise_invitable/mailer.rb +3 -7
  7. data/lib/devise_invitable/model.rb +53 -59
  8. data/lib/devise_invitable/parameter_sanitizer.rb +11 -0
  9. data/lib/devise_invitable/rails.rb +1 -0
  10. data/lib/devise_invitable/version.rb +1 -1
  11. data/lib/devise_invitable.rb +1 -0
  12. data/lib/generators/active_record/templates/migration.rb +3 -2
  13. data/test/functional/controller_helpers_test.rb +39 -0
  14. data/test/functional/registrations_controller_test.rb +59 -0
  15. data/test/generators/views_generator_test.rb +40 -0
  16. data/test/generators_test.rb +34 -0
  17. data/test/integration/invitation_remove_test.rb +29 -0
  18. data/test/integration/invitation_test.rb +222 -0
  19. data/test/integration_tests_helper.rb +48 -0
  20. data/test/mailers/invitation_mail_test.rb +69 -0
  21. data/test/model_tests_helper.rb +33 -0
  22. data/test/models/invitable_test.rb +558 -0
  23. data/test/models_test.rb +74 -0
  24. data/test/orm/active_record.rb +4 -0
  25. data/test/orm/mongoid.rb +20 -0
  26. data/test/rails_app/Rakefile +7 -0
  27. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  28. data/test/rails_app/app/controllers/application_controller.rb +10 -0
  29. data/test/rails_app/app/controllers/free_invitations_controller.rb +6 -0
  30. data/test/rails_app/app/controllers/home_controller.rb +4 -0
  31. data/test/rails_app/app/controllers/users_controller.rb +12 -0
  32. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  33. data/test/rails_app/app/models/admin.rb +23 -0
  34. data/test/rails_app/app/models/octopussy.rb +15 -0
  35. data/test/rails_app/app/models/user.rb +56 -0
  36. data/test/rails_app/app/views/admins/new.html.erb +12 -0
  37. data/test/rails_app/app/views/free_invitations/new.html.erb +12 -0
  38. data/test/rails_app/app/views/home/index.html.erb +0 -0
  39. data/test/rails_app/app/views/layouts/application.html.erb +16 -0
  40. data/test/rails_app/app/views/users/invitations/new.html.erb +15 -0
  41. data/test/rails_app/config/application.rb +24 -0
  42. data/test/rails_app/config/boot.rb +11 -0
  43. data/test/rails_app/config/database.yml +22 -0
  44. data/test/rails_app/config/environment.rb +5 -0
  45. data/test/rails_app/config/environments/development.rb +25 -0
  46. data/test/rails_app/config/environments/production.rb +49 -0
  47. data/test/rails_app/config/environments/test.rb +33 -0
  48. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  49. data/test/rails_app/config/initializers/devise.rb +213 -0
  50. data/test/rails_app/config/initializers/inflections.rb +10 -0
  51. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  52. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  53. data/test/rails_app/config/initializers/session_store.rb +8 -0
  54. data/test/rails_app/config/initializers/wrap_parameters.rb +14 -0
  55. data/test/rails_app/config/locales/devise.en.yml +57 -0
  56. data/test/rails_app/config/locales/en.yml +14 -0
  57. data/test/rails_app/config/routes.rb +9 -0
  58. data/test/rails_app/config.ru +4 -0
  59. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +39 -0
  60. data/test/rails_app/mongoid.yml +10 -0
  61. data/test/rails_app/script/rails +6 -0
  62. data/test/routes_test.rb +20 -0
  63. data/test/test_helper.rb +24 -0
  64. metadata +135 -47
  65. data/app/controllers/devise_invitable/registrations_controller.rb~ +0 -15
  66. data/lib/devise_invitable/controllers/helpers.rb~ +0 -21
  67. data/lib/devise_invitable/controllers/registrations.rb~ +0 -21
  68. data/lib/devise_invitable/model.rb~ +0 -224
  69. data/lib/devise_invitable/rails.rb~ +0 -21
  70. data/lib/devise_invitable.rb~ +0 -65
@@ -0,0 +1,34 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
2
+ require 'test_helper'
3
+ require 'rails/generators'
4
+ require 'generators/devise_invitable/devise_invitable_generator'
5
+
6
+ class GeneratorsTest < ActiveSupport::TestCase
7
+ RAILS_APP_PATH = File.expand_path("../rails_app", __FILE__)
8
+
9
+ test "rails g should include the 6 generators" do
10
+ @output = `cd #{RAILS_APP_PATH} && rails g`
11
+ generators = %w/devise_invitable devise_invitable:form_for devise_invitable:install devise_invitable:invitation_views devise_invitable:simple_form_for devise_invitable:views/
12
+
13
+ generators.each do |generator|
14
+ @output.include? generator
15
+ end
16
+ end
17
+
18
+ test "rails g devise_invitable:install" do
19
+ @output = `cd #{RAILS_APP_PATH} && rails g devise_invitable:install -p`
20
+ assert @output.match(%r{(inject|insert).+ config/initializers/devise\.rb\n})
21
+ assert @output.match(%r|create.+ config/locales/devise_invitable\.en\.yml\n|)
22
+ end
23
+
24
+ test "rails g devise_invitable Octopussy" do
25
+ @output = `cd #{RAILS_APP_PATH} && rails g devise_invitable Octopussy -p`
26
+ assert @output.match(%r{(inject|insert).+ app/models/octopussy\.rb\n})
27
+ assert @output.match(%r|invoke.+ #{DEVISE_ORM}\n|)
28
+ if DEVISE_ORM == :active_record
29
+ assert @output.match(%r|create.+ db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
30
+ elsif DEVISE_ORM == :mongoid
31
+ assert !@output.match(%r|create.+ db/migrate/\d{14}_devise_invitable_add_to_octopussies\.rb\n|)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+ require 'integration_tests_helper'
3
+
4
+ class InvitationRemoveTest < ActionDispatch::IntegrationTest
5
+
6
+ test 'invited user can choose to remove his account/invite' do
7
+ user = User.invite!(:email => "valid@email.com")
8
+
9
+ # remove!
10
+ visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
11
+ assert_equal root_path, current_path
12
+ assert page.has_css?('p#notice', :text => 'Your invitation was removed.')
13
+
14
+ # try to remove again!
15
+ visit remove_user_invitation_path(:invitation_token => Thread.current[:token])
16
+ assert_equal root_path, current_path
17
+ assert page.has_css?('p#alert', :text => 'The invitation token provided is not valid!')
18
+ end
19
+
20
+ test 'accepted user cannot remove his account (by using the original invitation token)' do
21
+ user = User.invite!(:email => "valid@email.com")
22
+ saved_token = Thread.current[:token]
23
+ user.accept_invitation!
24
+
25
+ visit remove_user_invitation_path(:invitation_token => saved_token)
26
+ assert_equal root_path, current_path
27
+ assert page.has_css?('p#alert', :text => 'The invitation token provided is not valid!')
28
+ end
29
+ end
@@ -0,0 +1,222 @@
1
+ require 'test_helper'
2
+ require 'integration_tests_helper'
3
+
4
+ class InvitationTest < ActionDispatch::IntegrationTest
5
+ def teardown
6
+ Capybara.reset_sessions!
7
+ end
8
+
9
+ def send_invitation(url = new_user_invitation_path, &block)
10
+ visit url
11
+
12
+ fill_in 'user_email', :with => 'user@test.com'
13
+ yield if block_given?
14
+ click_button 'Send an invitation'
15
+ end
16
+
17
+ def set_password(options={}, &block)
18
+ unless options[:visit] == false
19
+ visit accept_user_invitation_path(:invitation_token => options[:invitation_token])
20
+ end
21
+
22
+ fill_in 'user_password', :with => '987654321'
23
+ fill_in 'user_password_confirmation', :with => '987654321'
24
+ yield if block_given?
25
+ click_button options[:button] || 'Set my password'
26
+ end
27
+
28
+ test 'not authenticated user should be able to send a free invitation' do
29
+ send_invitation new_free_invitation_path
30
+ assert_equal root_path, current_path
31
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
32
+ end
33
+
34
+ test 'not authenticated user should not be able to send an invitation' do
35
+ get new_user_invitation_path
36
+ assert_redirected_to new_user_session_path
37
+ end
38
+
39
+ test 'authenticated user should be able to send an invitation' do
40
+ sign_in_as_user
41
+
42
+ send_invitation
43
+ assert_equal root_path, current_path
44
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
45
+ end
46
+
47
+ test 'authenticated user with existing email should receive an error message' do
48
+ user = create_full_user
49
+ sign_in_as_user(user)
50
+ send_invitation do
51
+ fill_in 'user_email', :with => user.email
52
+ end
53
+
54
+ assert_equal user_invitation_path, current_path
55
+ assert page.has_css?("input[type=text][value='#{user.email}']")
56
+ assert page.has_css?('#error_explanation li', :text => 'Email has already been taken')
57
+ end
58
+
59
+ test 'authenticated user should not be able to visit edit invitation page' do
60
+ sign_in_as_user
61
+
62
+ visit accept_user_invitation_path
63
+
64
+ assert_equal root_path, current_path
65
+ end
66
+
67
+ test 'not authenticated user with invalid invitation token should not be able to set his password' do
68
+ user = User.invite!(:email => "valid@email.com")
69
+ user.accept_invitation!
70
+ visit accept_user_invitation_path(:invitation_token => 'invalid_token')
71
+
72
+ assert_equal root_path, current_path
73
+ assert page.has_css?('p#alert', :text => 'The invitation token provided is not valid!')
74
+ end
75
+
76
+ test 'not authenticated user with valid invitation token but invalid password should not be able to set his password' do
77
+ user = User.invite!(:email => "valid@email.com")
78
+ set_password :invitation_token => Thread.current[:token] do
79
+ fill_in 'Password confirmation', :with => 'other_password'
80
+ end
81
+ assert_equal user_invitation_path, current_path
82
+ assert page.has_css?('#error_explanation li', :text => /Password .*doesn\'t match/)
83
+ assert_blank user.encrypted_password
84
+ end
85
+
86
+ test 'not authenticated user with valid data should be able to change his password' do
87
+ user = User.invite!(:email => "valid@email.com")
88
+ set_password :invitation_token => Thread.current[:token]
89
+
90
+ assert_equal root_path, current_path
91
+ assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
92
+ assert user.reload.valid_password?('987654321')
93
+ end
94
+
95
+ test 'after entering invalid data user should still be able to set his password' do
96
+ user = User.invite!(:email => "valid@email.com")
97
+ set_password :invitation_token => Thread.current[:token] do
98
+ fill_in 'Password confirmation', :with => 'other_password'
99
+ end
100
+ assert_equal user_invitation_path, current_path
101
+ assert page.has_css?('#error_explanation')
102
+ assert_blank user.encrypted_password
103
+
104
+ set_password :visit => false
105
+ assert page.has_css?('p#notice', :text => 'Your password was set successfully. You are now signed in.')
106
+ assert user.reload.valid_password?('987654321')
107
+ end
108
+
109
+ test 'sign in user automatically after setting it\'s password' do
110
+ user = User.invite!(:email => "valid@email.com")
111
+ set_password :invitation_token => Thread.current[:token]
112
+ assert_equal root_path, current_path
113
+ end
114
+
115
+ test 'clear token and set invitation_accepted_at after recover password instead of accept_invitation' do
116
+ user = User.invite!(:email => "valid@email.com")
117
+
118
+ visit new_user_password_path
119
+ fill_in 'user_email', :with => 'valid@email.com'
120
+ click_button 'Send me reset password instructions'
121
+
122
+ visit edit_user_password_path(:reset_password_token => Thread.current[:token])
123
+ set_password :visit => false, :button => 'Change my password'
124
+
125
+ user.reload
126
+ assert_nil user.invitation_token
127
+ assert user.invitation_accepted_at
128
+ end
129
+
130
+ test 'user with invites left should be able to send an invitation' do
131
+ User.stubs(:invitation_limit).returns(1)
132
+
133
+ user = create_full_user
134
+ user.invitation_limit = 1
135
+ user.save!
136
+ sign_in_as_user(user)
137
+
138
+ assert_difference 'User.count' do
139
+ send_invitation
140
+ end
141
+ assert_equal root_path, current_path
142
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
143
+ user = User.find(user.id)
144
+ assert !user.has_invitations_left?
145
+ end
146
+
147
+ test 'user with no invites left should not be able to send an invitation' do
148
+ User.stubs(:invitation_limit).returns(1)
149
+
150
+ user = create_full_user
151
+ user.invitation_limit = 0
152
+ user.save!
153
+ sign_in_as_user(user)
154
+
155
+ assert_no_difference 'User.count' do
156
+ send_invitation
157
+ end
158
+ assert_equal user_invitation_path, current_path
159
+ assert page.has_css?('p#alert', :text => 'No invitations remaining')
160
+ end
161
+
162
+ test 'user with nil invitation_limit should default to User.invitation_limit' do
163
+ User.stubs(:invitation_limit).returns(3)
164
+
165
+ user = create_full_user
166
+ assert_nil user[:invitation_limit]
167
+ assert_equal 3, user.invitation_limit
168
+ sign_in_as_user(user)
169
+
170
+ send_invitation
171
+ assert_equal root_path, current_path
172
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
173
+ user = User.find(user.id)
174
+ assert_equal 2, user.invitation_limit
175
+ end
176
+
177
+ test 'should not decrement invitation limit when trying to invite again a user which is invited' do
178
+ User.stubs(:invitation_limit).returns(3)
179
+
180
+ user = create_full_user
181
+ assert_nil user[:invitation_limit]
182
+ assert_equal 3, user.invitation_limit
183
+ sign_in_as_user(user)
184
+
185
+ send_invitation
186
+ assert_equal root_path, current_path
187
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
188
+ user = User.find(user.id)
189
+ assert_equal 2, user.invitation_limit
190
+
191
+ send_invitation
192
+ assert_equal root_path, current_path
193
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
194
+ user = User.find(user.id)
195
+ assert_equal 2, user.invitation_limit
196
+ end
197
+
198
+ test 'invited_by should be set when user invites someone' do
199
+ user = create_full_user
200
+ sign_in_as_user(user)
201
+ send_invitation
202
+
203
+ invited_user = User.where(:email => 'user@test.com').first
204
+ assert invited_user
205
+ assert_equal user, invited_user.invited_by
206
+ end
207
+
208
+ test 'authenticated user should not be able to send an admin invitation' do
209
+ sign_in_as_user
210
+
211
+ get new_admin_path
212
+ assert_redirected_to new_admin_session_path
213
+ end
214
+
215
+ test 'authenticated admin should be able to send an admin invitation' do
216
+ sign_in_as_user Admin.create(:email => 'admin@test.com', :password => '123456', :password_confirmation => '123456')
217
+
218
+ send_invitation new_admin_path
219
+ assert_equal root_path, current_path
220
+ assert page.has_css?('p#notice', :text => 'An invitation email has been sent to user@test.com.')
221
+ end
222
+ end
@@ -0,0 +1,48 @@
1
+ class ActionController::IntegrationTest
2
+
3
+ def warden
4
+ request.env['warden']
5
+ end
6
+
7
+ def create_full_user
8
+ @user ||= begin
9
+ user = User.create!(
10
+ :username => 'usertest',
11
+ :email => 'fulluser@test.com',
12
+ :password => '123456',
13
+ :password_confirmation => '123456',
14
+ :created_at => Time.now.utc
15
+ )
16
+ user.confirm!
17
+ user
18
+ end
19
+ end
20
+
21
+ def sign_in_as_user(user = nil)
22
+ user ||= create_full_user
23
+ resource_name = user.class.name.underscore
24
+ visit send("new_#{resource_name}_session_path")
25
+ fill_in "#{resource_name}_email", :with => user.email
26
+ fill_in "#{resource_name}_password", :with => user.password
27
+ click_button 'Sign in'
28
+ end
29
+
30
+ # Fix assert_redirect_to in integration sessions because they don't take into
31
+ # account Middleware redirects.
32
+ #
33
+ def assert_redirected_to(url)
34
+ assert [301, 302].include?(@integration_session.status),
35
+ "Expected status to be 301 or 302, got #{@integration_session.status}"
36
+
37
+ url = prepend_host(url)
38
+ location = prepend_host(@integration_session.headers["Location"])
39
+ assert_equal url, location
40
+ end
41
+
42
+ protected
43
+
44
+ def prepend_host(url)
45
+ url = "http://#{request.host}#{url}" if url[0] == ?/
46
+ url
47
+ end
48
+ end
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+ require 'model_tests_helper'
3
+
4
+ class InvitationMailTest < ActionMailer::TestCase
5
+
6
+ def setup
7
+ setup_mailer
8
+ Devise.mailer_sender = 'test@example.com'
9
+ end
10
+
11
+ def user
12
+ @user ||= User.invite!(:email => "valid@email.com")
13
+ end
14
+
15
+ def mail
16
+ @mail ||= begin
17
+ user
18
+ ActionMailer::Base.deliveries.last
19
+ end
20
+ end
21
+
22
+ test 'email sent after reseting the user password' do
23
+ assert_not_nil mail
24
+ end
25
+
26
+ test 'content type should be set to html' do
27
+ assert_equal 'text/html; charset=UTF-8', mail.content_type
28
+ end
29
+
30
+ test 'send invitation to the user email' do
31
+ assert_equal [user.email], mail.to
32
+ end
33
+
34
+ test 'setup sender from configuration' do
35
+ assert_equal ['test@example.com'], mail.from
36
+ end
37
+
38
+ test 'setup subject from I18n' do
39
+ store_translations :en, :devise => { :mailer => { :invitation_instructions => { :subject => 'Localized Invitation' } } } do
40
+ assert_equal 'Localized Invitation', mail.subject
41
+ end
42
+ end
43
+
44
+ test 'subject namespaced by model' do
45
+ store_translations :en, :devise => { :mailer => { :invitation_instructions => { :user_subject => 'User Invitation' } } } do
46
+ assert_equal 'User Invitation', mail.subject
47
+ end
48
+ end
49
+
50
+ test 'body should have user info' do
51
+ assert_match /#{user.email}/, mail.body.decoded
52
+ end
53
+
54
+ test 'body should have link to confirm the account' do
55
+ host = ActionMailer::Base.default_url_options[:host]
56
+ body = mail.body.decoded
57
+ invitation_url_regexp = %r{<a href=\"http://#{host}/users/invitation/accept\?invitation_token=#{Thread.current[:token]}">}
58
+ assert_match invitation_url_regexp, body
59
+ end
60
+
61
+ test 'body should have link to confirm the account on resend' do
62
+ host = ActionMailer::Base.default_url_options[:host]
63
+ user
64
+ @user = User.find(user.id).invite!
65
+ body = mail.body.decoded
66
+ invitation_url_regexp = %r{<a href=\"http://#{host}/users/invitation/accept\?invitation_token=#{Thread.current[:token]}">}
67
+ assert_match invitation_url_regexp, body
68
+ end
69
+ end
@@ -0,0 +1,33 @@
1
+ class ActiveSupport::TestCase
2
+ def setup_mailer
3
+ ActionMailer::Base.deliveries = []
4
+ end
5
+
6
+ def store_translations(locale, translations, &block)
7
+ begin
8
+ I18n.backend.store_translations locale, translations
9
+ yield
10
+ ensure
11
+ I18n.reload!
12
+ end
13
+ end
14
+
15
+ # Helpers for creating new users
16
+ #
17
+ def generate_unique_email
18
+ @@email_count ||= 0
19
+ @@email_count += 1
20
+ "test#{@@email_count}@email.com"
21
+ end
22
+
23
+ def valid_attributes(attributes={})
24
+ { :email => generate_unique_email,
25
+ :password => '123456',
26
+ :password_confirmation => '123456' }.update(attributes)
27
+ end
28
+
29
+ def new_user(attributes={})
30
+ User.new(valid_attributes(attributes))
31
+ end
32
+
33
+ end