rockstart 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +100 -0
  4. data/Rakefile +19 -0
  5. data/lib/generators/rockstart/USAGE +13 -0
  6. data/lib/generators/rockstart/devise/USAGE +9 -0
  7. data/lib/generators/rockstart/devise/devise_generator.rb +258 -0
  8. data/lib/generators/rockstart/devise/templates/controllers/passwords_controller.rb +56 -0
  9. data/lib/generators/rockstart/devise/templates/controllers/registrations_controller.rb +88 -0
  10. data/lib/generators/rockstart/devise/templates/controllers/sessions_controller.rb +32 -0
  11. data/lib/generators/rockstart/devise/templates/create_user_migration.rb.tt +11 -0
  12. data/lib/generators/rockstart/devise/templates/models/user.rb +42 -0
  13. data/lib/generators/rockstart/devise/templates/spec/factories/users.rb +17 -0
  14. data/lib/generators/rockstart/devise/templates/spec/models/user_spec.rb +64 -0
  15. data/lib/generators/rockstart/devise/templates/spec/requests/users/passwords_spec.rb +202 -0
  16. data/lib/generators/rockstart/devise/templates/spec/requests/users/registrations_spec.rb +445 -0
  17. data/lib/generators/rockstart/devise/templates/spec/requests/users/sessions_spec.rb +171 -0
  18. data/lib/generators/rockstart/devise/templates/spec/support/devise_request_spec_helper.rb +29 -0
  19. data/lib/generators/rockstart/devise/templates/translations.en.yml +4 -0
  20. data/lib/generators/rockstart/docker/USAGE +10 -0
  21. data/lib/generators/rockstart/docker/docker_generator.rb +86 -0
  22. data/lib/generators/rockstart/docker/templates/app/Dockerfile-app +47 -0
  23. data/lib/generators/rockstart/docker/templates/docker-compose.test.yml +29 -0
  24. data/lib/generators/rockstart/docker/templates/docker-compose.yml +47 -0
  25. data/lib/generators/rockstart/docker/templates/dockerignore +16 -0
  26. data/lib/generators/rockstart/docker/templates/dotenv.docker.tt +4 -0
  27. data/lib/generators/rockstart/docker/templates/localhost_domains.ext.tt +7 -0
  28. data/lib/generators/rockstart/docker/templates/setup-localhost.tt +27 -0
  29. data/lib/generators/rockstart/docker/templates/web/Dockerfile-web +15 -0
  30. data/lib/generators/rockstart/docker/templates/web/nginx.conf +62 -0
  31. data/lib/generators/rockstart/frontend_helpers/USAGE +8 -0
  32. data/lib/generators/rockstart/frontend_helpers/frontend_helpers_generator.rb +65 -0
  33. data/lib/generators/rockstart/frontend_helpers/templates/application_urls.rb +26 -0
  34. data/lib/generators/rockstart/frontend_helpers/templates/application_urls_helper.rb +20 -0
  35. data/lib/generators/rockstart/frontend_helpers/templates/titles.en.yml.tt +5 -0
  36. data/lib/generators/rockstart/logging/USAGE +8 -0
  37. data/lib/generators/rockstart/logging/logging_generator.rb +12 -0
  38. data/lib/generators/rockstart/logging/templates/rockstart/lograge_initializer.rb +50 -0
  39. data/lib/generators/rockstart/postgres/USAGE +8 -0
  40. data/lib/generators/rockstart/postgres/postgres_generator.rb +32 -0
  41. data/lib/generators/rockstart/postgres/templates/config/database.yml.tt +18 -0
  42. data/lib/generators/rockstart/postgres/templates/migration.rb.tt +7 -0
  43. data/lib/generators/rockstart/pundit/USAGE +8 -0
  44. data/lib/generators/rockstart/pundit/pundit_generator.rb +32 -0
  45. data/lib/generators/rockstart/pundit/templates/app/controllers/concerns/pundit_error_handling.rb +29 -0
  46. data/lib/generators/rockstart/pundit/templates/app/policies/application_policy.rb +71 -0
  47. data/lib/generators/rockstart/pundit/templates/app/policies/user_policy.rb +47 -0
  48. data/lib/generators/rockstart/pundit/templates/config/locales/pundit.en.yml +6 -0
  49. data/lib/generators/rockstart/pundit/templates/lib/templates/pundit/policy/policy.rb +36 -0
  50. data/lib/generators/rockstart/pundit/templates/lib/templates/rspec/policy/policy_spec.rb +58 -0
  51. data/lib/generators/rockstart/pundit/templates/spec/policies/user_policy_spec.rb +95 -0
  52. data/lib/generators/rockstart/pundit/templates/spec/support/pundit_matchers.rb +7 -0
  53. data/lib/generators/rockstart/quality/USAGE +10 -0
  54. data/lib/generators/rockstart/quality/quality_generator.rb +28 -0
  55. data/lib/generators/rockstart/quality/templates/quality.rake +4 -0
  56. data/lib/generators/rockstart/quality/templates/rubocop.rake +4 -0
  57. data/lib/generators/rockstart/quality/templates/rubocop.yml +45 -0
  58. data/lib/generators/rockstart/rockstart_generator.rb +77 -0
  59. data/lib/generators/rockstart/rspec/USAGE +8 -0
  60. data/lib/generators/rockstart/rspec/rspec_generator.rb +70 -0
  61. data/lib/generators/rockstart/rspec/templates/dotenv.development +1 -0
  62. data/lib/generators/rockstart/rspec/templates/dotenv.test +1 -0
  63. data/lib/generators/rockstart/rspec/templates/rspec_templates/model/model_spec.rb +13 -0
  64. data/lib/generators/rockstart/rspec/templates/support/factory_bot.rb +6 -0
  65. data/lib/generators/rockstart/rspec/templates/support/shoulda_matchers.rb +9 -0
  66. data/lib/generators/rockstart/rspec/templates/support/test_helpers.rb +9 -0
  67. data/lib/generators/rockstart/scaffold_templates/USAGE +8 -0
  68. data/lib/generators/rockstart/scaffold_templates/scaffold_templates_generator.rb +39 -0
  69. data/lib/generators/rockstart/scaffold_templates/templates/api_controller.rb.tt +96 -0
  70. data/lib/generators/rockstart/scaffold_templates/templates/controller.rb.tt +126 -0
  71. data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/api_request_spec.rb +139 -0
  72. data/lib/generators/rockstart/scaffold_templates/templates/rspec/scaffold/request_spec.rb +408 -0
  73. data/lib/generators/rockstart/security/USAGE +13 -0
  74. data/lib/generators/rockstart/security/security_generator.rb +108 -0
  75. data/lib/generators/rockstart/security/templates/brakeman.rake +6 -0
  76. data/lib/generators/rockstart/security/templates/bundler_audit.rake +4 -0
  77. data/lib/generators/rockstart/security/templates/cache_support.rb +18 -0
  78. data/lib/generators/rockstart/security/templates/content_security_policy_initializer.rb.tt +56 -0
  79. data/lib/generators/rockstart/security/templates/content_security_spec.rb.tt +83 -0
  80. data/lib/generators/rockstart/security/templates/csp_violations_controller.rb +39 -0
  81. data/lib/generators/rockstart/security/templates/rack_attack.rb +98 -0
  82. data/lib/generators/rockstart/security/templates/security.rake +9 -0
  83. data/lib/generators/rockstart/security/templates/session_store_initializer.rb.tt +7 -0
  84. data/lib/generators/rockstart/smtp_mailer/USAGE +8 -0
  85. data/lib/generators/rockstart/smtp_mailer/smtp_mailer_generator.rb +30 -0
  86. data/lib/generators/rockstart/smtp_mailer/templates/config/initializers/action_mailer.rb +10 -0
  87. data/lib/generators/rockstart/tailwindcss/USAGE +8 -0
  88. data/lib/generators/rockstart/tailwindcss/tailwindcss_generator.rb +30 -0
  89. data/lib/generators/rockstart/tailwindcss/templates/application.css +3 -0
  90. data/lib/generators/rockstart/tailwindcss/templates/postcss.config.js +32 -0
  91. data/lib/rockstart/base_generator.rb +32 -0
  92. data/lib/rockstart/env.rb +16 -0
  93. data/lib/rockstart/railtie.rb +6 -0
  94. data/lib/rockstart/version.rb +5 -0
  95. data/lib/rockstart.rb +9 -0
  96. data/lib/tasks/rockstart_tasks.rake +5 -0
  97. metadata +187 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe User, type: :model do
6
+ # email:string
7
+ it { is_expected.to have_db_column(:email) }
8
+ # encrypted_password:string
9
+ it { is_expected.to have_db_column(:encrypted_password) }
10
+ # name:string
11
+ it { is_expected.to have_db_column(:name) }
12
+ it { is_expected.not_to validate_presence_of(:name) }
13
+ # admin:boolean
14
+ it { is_expected.to have_db_column(:admin).with_options(default: false) }
15
+ # deleted_at:datetime
16
+ it { is_expected.to have_db_column(:deleted_at).of_type(:datetime) }
17
+
18
+ describe "#given" do
19
+ it "returns the given name from the name" do
20
+ user = User.new(name: "John Smith")
21
+ expect(user.given).to eq "John"
22
+ end
23
+
24
+ it "handles nil name valeus" do
25
+ user = User.new(name: nil)
26
+ expect(user.given).to be_nil
27
+ end
28
+ end
29
+
30
+ describe "#family" do
31
+ it "returns the family name from the name" do
32
+ user = User.new(name: "John Smith")
33
+ expect(user.family).to eq "Smith"
34
+ end
35
+
36
+ it "handles nil name valeus" do
37
+ user = User.new(name: nil)
38
+ expect(user.family).to be_nil
39
+ end
40
+ end
41
+
42
+ describe "#to_s" do
43
+ it "returns the persisted name of the user" do
44
+ user = User.new(name: "John Smith")
45
+ user.changes_applied
46
+ expect(user.to_s).to eq "John Smith"
47
+
48
+ user.name = "Jack Smith"
49
+ expect(user.to_s).to eq "John Smith"
50
+ end
51
+
52
+ it "falls back to a generic label when name is not present" do
53
+ user = build_stubbed(:user, name: nil)
54
+ allow(user).to receive(:id?).and_return(true)
55
+ allow(user).to receive(:id).and_return(1234)
56
+ expect(user.to_s).to eq "User #1234"
57
+ end
58
+
59
+ it "returns a generic label when user is not persisted" do
60
+ user = User.new(name: nil)
61
+ expect(user.to_s).to eq "Guest User"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe "Users::Passwords", type: :request do
6
+ describe "GET /users/password/new" do
7
+ context "as a guest" do
8
+ it "renders a successful response" do
9
+ get new_user_password_path
10
+ expect(response).to be_successful
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "POST /users/password" do
16
+ context "with a known user email" do
17
+ let(:valid_params) do
18
+ {
19
+ user: {
20
+ email: create(:user).email
21
+ }
22
+ }
23
+ end
24
+
25
+ it "redirects the user to the login page" do
26
+ post user_password_path, params: valid_params
27
+ expect(response).to redirect_to(new_user_session_path)
28
+
29
+ follow_redirect!
30
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.passwords.send_paranoid_instructions"))
31
+ end
32
+
33
+ it "sends a password reset email" do
34
+ expect do
35
+ post user_password_path, params: valid_params
36
+ end.to change(ActionMailer::Base.deliveries, :count).by(1)
37
+
38
+ delivery = ActionMailer::Base.deliveries.last
39
+ expect(delivery.to).to eq [valid_params.dig(:user, :email)]
40
+ expect(delivery.subject).to eq t("devise.mailer.reset_password_instructions.subject")
41
+
42
+ # Verify the reset password token is present
43
+ expected_url = edit_user_password_path(reset_password_token: "")
44
+ expect(delivery.body.raw_source).to have_selector("a[href*=\"#{expected_url}\"]")
45
+ end
46
+ end
47
+
48
+ context "with an unknown email address" do
49
+ let(:unknown_user_params) do
50
+ {
51
+ user: {
52
+ email: Faker::Internet.email
53
+ }
54
+ }
55
+ end
56
+
57
+ it "redirects the user to the login page" do
58
+ post user_password_path, params: unknown_user_params
59
+ expect(response).to redirect_to(url_for_authentication)
60
+
61
+ follow_redirect!
62
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.passwords.send_paranoid_instructions"))
63
+ end
64
+
65
+ it "does not send any emails" do
66
+ expect do
67
+ post user_password_path, params: unknown_user_params
68
+ end.not_to change(ActionMailer::Base.deliveries, :count)
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "GET /users/password/edit" do
74
+ context "with a valid password reset token" do
75
+ let(:valid_reset_password_token) do
76
+ create(:user).send(:set_reset_password_token)
77
+ end
78
+
79
+ it "redirects back to the password reset page without the reset_password_token param" do
80
+ get edit_user_password_path, params: { reset_password_token: valid_reset_password_token }
81
+ expect(response).to redirect_to(edit_user_password_path)
82
+
83
+ follow_redirect!
84
+ expect(response.body).to have_selector("form input[name='user[reset_password_token]'][value='#{valid_reset_password_token}']", visible: false)
85
+ end
86
+ end
87
+
88
+ context "with an invalid password reset token" do
89
+ let(:invalid_reset_password_token) do
90
+ Faker::Lorem.words(number: 2).join
91
+ end
92
+
93
+ it "processes the invalid reset password token the same as a valid token" do
94
+ get edit_user_password_path, params: { reset_password_token: invalid_reset_password_token }
95
+ expect(response).to redirect_to(edit_user_password_path)
96
+
97
+ follow_redirect!
98
+ expect(response.body).to have_selector("form input[name='user[reset_password_token]'][value='#{invalid_reset_password_token}']", visible: false)
99
+ end
100
+ end
101
+
102
+ context "with no password request token" do
103
+ it "redirects the user with a warning" do
104
+ get edit_user_password_path
105
+ expect(response).to redirect_to(new_user_session_path)
106
+
107
+ follow_redirect!
108
+ expect(response.body).to have_selector(".alert-alert", text: t("devise.passwords.no_token"))
109
+ end
110
+ end
111
+ end
112
+
113
+ describe "PUT /users/password" do
114
+ context "with valid password change params" do
115
+ let(:user) { create(:user) }
116
+ let(:valid_reset_password_token) do
117
+ user.send(:set_reset_password_token)
118
+ end
119
+ let(:valid_password) do
120
+ Faker::Lorem.words(number: 3).join
121
+ end
122
+
123
+ let(:valid_password_change_params) do
124
+ {
125
+ user: {
126
+ reset_password_token: valid_reset_password_token,
127
+ password: valid_password,
128
+ password_confirmation: valid_password
129
+ }
130
+ }
131
+ end
132
+
133
+ it "redirects the user to the dashboard with a notice" do
134
+ put user_password_path, params: valid_password_change_params
135
+ expect(response).to redirect_to(root_url)
136
+
137
+ follow_redirect!
138
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.passwords.updated"))
139
+ end
140
+
141
+ it "changes the users password" do
142
+ put user_password_path, params: valid_password_change_params
143
+
144
+ user.reload
145
+ expect(user.valid_password?(valid_password)).to be(true)
146
+ end
147
+ end
148
+
149
+ context "with non-matching passwords" do
150
+ let(:user) { create(:user) }
151
+ let(:valid_reset_password_token) do
152
+ user.send(:set_reset_password_token)
153
+ end
154
+ let(:valid_password) do
155
+ Faker::Lorem.words(number: 3).join
156
+ end
157
+
158
+ let(:valid_password_change_params) do
159
+ {
160
+ user: {
161
+ reset_password_token: valid_reset_password_token,
162
+ password: valid_password,
163
+ password_confirmation: valid_password.reverse
164
+ }
165
+ }
166
+ end
167
+
168
+ it "responds with success (displays a form with errors)" do
169
+ put user_password_path, params: valid_password_change_params
170
+
171
+ expect(response).to be_successful
172
+ expect(response.body).to have_selector(".field_with_errors")
173
+ end
174
+ end
175
+
176
+ context "with an invalid password reset token" do
177
+ let(:invalid_reset_password_token) do
178
+ Faker::Lorem.words(number: 2).join
179
+ end
180
+ let(:valid_password) do
181
+ Faker::Lorem.words(number: 3).join
182
+ end
183
+
184
+ let(:invalid_password_change_params) do
185
+ {
186
+ user: {
187
+ reset_password_token: invalid_reset_password_token,
188
+ password: valid_password,
189
+ password_confirmation: valid_password.reverse
190
+ }
191
+ }
192
+ end
193
+
194
+ it "responds with success (displays a form with errors)" do
195
+ put user_password_path, params: invalid_password_change_params
196
+
197
+ expect(response).to be_successful
198
+ expect(response.body).to have_content("Reset password token is invalid")
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,445 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe "Users::Registrations", type: :request do
6
+ describe "GET /users/sign_up" do
7
+ context "as a guest" do
8
+ it "renders a successful response" do
9
+ get new_user_registration_path
10
+ expect(response).to be_successful
11
+ end
12
+ end
13
+
14
+ context "as an authenticated user" do
15
+ let(:authenticated_user) { create(:user) }
16
+
17
+ before do
18
+ sign_in(authenticated_user)
19
+ end
20
+
21
+ it "redirects to the dashboard with a warning" do
22
+ get new_user_registration_path
23
+ expect(response).to redirect_to(url_for_user_dashboard)
24
+
25
+ follow_redirect!
26
+ expect(response.body).to have_selector(".alert-alert", text: t("devise.failure.already_authenticated"))
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "POST /users", :cache_testing do
32
+ context "with valid create user params" do
33
+ let(:valid_password) { Faker::Internet.password }
34
+ let(:valid_registration_params) do
35
+ {
36
+ user: {
37
+ email: Faker::Internet.email,
38
+ password: valid_password,
39
+ password_confirmation: valid_password
40
+ }
41
+ }
42
+ end
43
+
44
+ it "redirects to the dashboard with a notice" do
45
+ post user_registration_path, params: valid_registration_params
46
+ expect(response).to redirect_to(url_for_user_dashboard)
47
+
48
+ follow_redirect!
49
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.registrations.signed_up"))
50
+ end
51
+
52
+ it "does not allow authenticated users" do
53
+ sign_in create(:user)
54
+
55
+ post user_registration_path, params: valid_registration_params
56
+ expect(response).to redirect_to(url_for_user_dashboard)
57
+
58
+ follow_redirect!
59
+ expect(response.body).to have_selector(".alert-alert", text: t("devise.failure.already_authenticated"))
60
+ end
61
+ end
62
+
63
+ context "with mismatching passwords" do
64
+ let(:invalid_registration_params) do
65
+ {
66
+ user: {
67
+ email: Faker::Internet.email,
68
+ password: Faker::Internet.password,
69
+ password_confirmation: Faker::Lorem.words(number: 3).join
70
+ }
71
+ }
72
+ end
73
+
74
+ it "renders the form with an error" do
75
+ post user_registration_path, params: invalid_registration_params
76
+ expect(response).to be_successful
77
+
78
+ expect(response.body).to have_content("Password confirmation doesn't match Password")
79
+ end
80
+ end
81
+
82
+ context "with an email address matching an existing user" do
83
+ let(:existing_user) { create(:user) }
84
+ let(:valid_password) { Faker::Internet.password }
85
+ let(:existing_registration_params) do
86
+ {
87
+ user: {
88
+ email: existing_user.email,
89
+ password: valid_password,
90
+ password_confirmation: valid_password
91
+ }
92
+ }
93
+ end
94
+
95
+ it "renders the form with an error" do
96
+ post user_registration_path, params: existing_registration_params
97
+ expect(response).to be_successful
98
+
99
+ expect(response.body).to have_content("Email has already been taken")
100
+ end
101
+ end
102
+
103
+ it "rate limits requests based off ip address" do
104
+ valid_password = Faker::Internet.password
105
+
106
+ 5.times do
107
+ post user_registration_path, params: {
108
+ user: {
109
+ email: Faker::Internet.email,
110
+ password: valid_password,
111
+ password_confirmation: valid_password
112
+ }
113
+ }
114
+ end
115
+
116
+ post user_registration_path, params: {
117
+ user: {
118
+ email: Faker::Internet.email,
119
+ password: valid_password,
120
+ password_confirmation: valid_password
121
+ }
122
+ }
123
+ expect(response).to have_http_status(:too_many_requests)
124
+ end
125
+ end
126
+
127
+ describe "GET /users/edit" do
128
+ context "as an authenticated user" do
129
+ let(:authenticated_user) { create(:user) }
130
+
131
+ before do
132
+ sign_in(authenticated_user)
133
+ end
134
+
135
+ it "renders the edit user form" do
136
+ get edit_user_registration_path
137
+ expect(response).to be_successful
138
+ end
139
+ end
140
+
141
+ context "as a guest" do
142
+ it "redirects to the new user session path" do
143
+ get edit_user_registration_path
144
+ expect(response).to redirect_to(new_user_session_path)
145
+
146
+ follow_redirect!
147
+ expect(response.body).to have_selector(".alert-alert", text: t("devise.failure.unauthenticated"))
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "PUT /users", :cache_testing do
153
+ context "with update user email params" do
154
+ let(:original_email) { Faker::Internet.email }
155
+ let(:updated_email) { Faker::Internet.email }
156
+ let(:update_user_email_params) do
157
+ {
158
+ user: {
159
+ email: updated_email,
160
+ name: Faker::Name.name
161
+ }
162
+ }
163
+ end
164
+
165
+ context "as an authenticated user" do
166
+ let(:authenticated_user) { create(:user, email: original_email) }
167
+
168
+ before do
169
+ sign_in(authenticated_user)
170
+ end
171
+
172
+ it "redirects to the dashboard with a notice" do
173
+ put user_registration_path, params: update_user_email_params
174
+ expect(response).to redirect_to(url_for_user_dashboard)
175
+
176
+ follow_redirect!
177
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.registrations.updated"))
178
+ end
179
+
180
+ it "updates the email of the user " do
181
+ put user_registration_path, params: update_user_email_params
182
+
183
+ authenticated_user.reload
184
+ expect(authenticated_user.email).to eq updated_email
185
+ end
186
+
187
+ it "sends an email changed notification to the previous email" do
188
+ expect do
189
+ put user_registration_path, params: update_user_email_params
190
+ end.to change(ActionMailer::Base.deliveries, :count).by(1)
191
+
192
+ delivery = ActionMailer::Base.deliveries.last
193
+ expect(delivery.to).to eq [original_email]
194
+ expect(delivery.subject).to eq t("devise.mailer.email_changed.subject")
195
+ end
196
+
197
+ it "rate limits requests based off ip address" do
198
+ 5.times do
199
+ put user_registration_path, params: update_user_email_params
200
+ end
201
+
202
+ put user_registration_path, params: update_user_email_params
203
+ expect(response).to have_http_status(:too_many_requests)
204
+ end
205
+ end
206
+
207
+ context "as a guest" do
208
+ it "redirects to the new user session path" do
209
+ put user_registration_path, params: update_user_email_params
210
+ expect(response).to redirect_to(new_user_session_path)
211
+
212
+ follow_redirect!
213
+ expect(response.body).to have_selector(".alert-alert", text: t("devise.failure.unauthenticated"))
214
+ end
215
+ end
216
+ end
217
+
218
+ context "with update password params" do
219
+ let(:current_password) { Faker::Internet.password }
220
+ let(:updated_password) { Faker::Internet.password }
221
+ let(:update_user_password_params) do
222
+ {
223
+ user: {
224
+ current_password: current_password,
225
+ password: updated_password,
226
+ password_confirmation: updated_password
227
+ }
228
+ }
229
+ end
230
+
231
+ context "as an authenticated user" do
232
+ let(:authenticated_user) { create(:user, password: current_password) }
233
+
234
+ before do
235
+ sign_in(authenticated_user)
236
+ end
237
+
238
+ it "redirects to the dashboard with a notice" do
239
+ put user_registration_path, params: update_user_password_params
240
+ expect(response).to redirect_to(url_for_user_dashboard)
241
+
242
+ follow_redirect!
243
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.registrations.updated"))
244
+ end
245
+
246
+ it "updates the password of the user" do
247
+ put user_registration_path, params: update_user_password_params
248
+
249
+ authenticated_user.reload
250
+ expect(authenticated_user.valid_password?(updated_password)).to be(true)
251
+ end
252
+
253
+ it "sends a password changed notification to the previous email" do
254
+ expect do
255
+ put user_registration_path, params: update_user_password_params
256
+ end.to change(ActionMailer::Base.deliveries, :count).by(1)
257
+
258
+ delivery = ActionMailer::Base.deliveries.last
259
+ expect(delivery.to).to eq [authenticated_user.email]
260
+ expect(delivery.subject).to eq t("devise.mailer.password_change.subject")
261
+ end
262
+ end
263
+ end
264
+
265
+ context "with incorrect current password password params" do
266
+ let(:updated_password) { Faker::Internet.password }
267
+ let(:invalid_current_password_params) do
268
+ {
269
+ user: {
270
+ current_password: Faker::Internet.password,
271
+ password: updated_password,
272
+ password_confirmation: updated_password
273
+ }
274
+ }
275
+ end
276
+
277
+ context "as an authenticated user" do
278
+ let(:authenticated_user) { create(:user) }
279
+
280
+ before do
281
+ sign_in(authenticated_user)
282
+ end
283
+
284
+ it "renders the form with an error" do
285
+ put user_registration_path, params: invalid_current_password_params
286
+ expect(response).to be_successful
287
+
288
+ expect(response.body).to have_content("Current password is invalid")
289
+ end
290
+ end
291
+ end
292
+
293
+ context "with incorrect password confirmation params" do
294
+ let(:current_password) { Faker::Internet.password }
295
+ let(:invalid_password_confirmation_params) do
296
+ {
297
+ user: {
298
+ current_password: current_password,
299
+ password: Faker::Internet.password,
300
+ password_confirmation: Faker::Internet.password
301
+ }
302
+ }
303
+ end
304
+
305
+ context "as an authenticated user" do
306
+ let(:authenticated_user) { create(:user, password: current_password) }
307
+
308
+ before do
309
+ sign_in(authenticated_user)
310
+ end
311
+
312
+ it "renders the form with an error" do
313
+ put user_registration_path, params: invalid_password_confirmation_params
314
+ expect(response).to be_successful
315
+
316
+ expect(response.body).to have_content("Password confirmation doesn't match Password")
317
+ end
318
+ end
319
+ end
320
+
321
+ context "with no password confirmation param" do
322
+ let(:current_password) { Faker::Internet.password }
323
+ let(:no_password_confirmation_params) do
324
+ {
325
+ user: {
326
+ current_password: current_password,
327
+ password: Faker::Internet.password
328
+ }
329
+ }
330
+ end
331
+
332
+ context "as an authenticated user" do
333
+ let(:authenticated_user) { create(:user, password: current_password) }
334
+
335
+ before do
336
+ sign_in(authenticated_user)
337
+ end
338
+
339
+ it "renders the form with an error" do
340
+ put user_registration_path, params: no_password_confirmation_params
341
+ expect(response).to be_successful
342
+
343
+ expect(response.body).to have_content("Password confirmation doesn't match Password")
344
+ end
345
+ end
346
+ end
347
+
348
+ context "with update user details params" do
349
+ let(:existing_email) { Faker::Internet.email }
350
+ let(:updated_name) { Faker::Name.name }
351
+ let(:update_user_details_params) do
352
+ {
353
+ user: {
354
+ email: existing_email,
355
+ name: updated_name
356
+ }
357
+ }
358
+ end
359
+
360
+ context "as an authenticated user" do
361
+ let(:authenticated_user) { create(:user, email: existing_email) }
362
+
363
+ before do
364
+ sign_in(authenticated_user)
365
+ end
366
+
367
+ it "redirects to the dashboard with a notice" do
368
+ put user_registration_path, params: update_user_details_params
369
+ expect(response).to redirect_to(url_for_user_dashboard)
370
+
371
+ follow_redirect!
372
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.registrations.updated"))
373
+ end
374
+
375
+ it "updates the personal details of the user " do
376
+ put user_registration_path, params: update_user_details_params
377
+
378
+ authenticated_user.reload
379
+ expect(authenticated_user.email).to eq existing_email
380
+ expect(authenticated_user.name).to eq updated_name
381
+ end
382
+
383
+ it "does not send any emails" do
384
+ expect do
385
+ put user_registration_path, params: update_user_details_params
386
+ end.not_to change(ActionMailer::Base.deliveries, :count)
387
+ end
388
+ end
389
+ end
390
+ end
391
+
392
+ describe "DELETE /users" do
393
+ context "with an authenticated user" do
394
+ let(:authenticated_user) { create(:user) }
395
+
396
+ before do
397
+ sign_in(authenticated_user)
398
+ end
399
+
400
+ it "redirects to the registration page with a notice" do
401
+ delete user_registration_path
402
+ expect(response).to redirect_to(new_user_registration_path)
403
+
404
+ follow_redirect!
405
+ expect(response.body).to have_selector(".alert-notice", text: t("devise.registrations.destroyed"))
406
+ end
407
+
408
+ it "soft deletes the User" do
409
+ expect do
410
+ delete user_registration_path
411
+ end.not_to change(User, :count)
412
+
413
+ authenticated_user.reload
414
+ expect(authenticated_user).to be_deleted_at
415
+ expect(authenticated_user).not_to be_active_for_authentication
416
+ end
417
+ end
418
+ end
419
+
420
+ context "with an authenticated admin" do
421
+ let(:authenticated_admin) { create(:user, :admin) }
422
+
423
+ before do
424
+ sign_in(authenticated_admin)
425
+ end
426
+
427
+ it "redirects to the dashboard page with an error" do
428
+ delete user_registration_path
429
+ expect(response).to redirect_to(url_for_user_dashboard)
430
+
431
+ follow_redirect!
432
+ expect(response.body).to have_selector(".alert-error", text: t("pundit.user_policy.destroy?", default: t("pundit.default")))
433
+ end
434
+
435
+ it "does not soft delete the user" do
436
+ expect do
437
+ delete user_registration_path
438
+ end.not_to change(User, :count)
439
+
440
+ authenticated_admin.reload
441
+ expect(authenticated_admin).to be_active_for_authentication
442
+ expect(authenticated_admin).not_to be_deleted_at
443
+ end
444
+ end
445
+ end