rockstart 0.1.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 (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