From 461df4244e9157126d783d71e1652676da4dedff Mon Sep 17 00:00:00 2001 From: Sergio-e <33036058+Sergio-e@users.noreply.github.com> Date: Wed, 10 Jul 2024 19:01:05 -0600 Subject: [PATCH 1/2] Fixes & improvements to user authentication --- app/controllers/application_controller.rb | 2 - app/controllers/concerns/authentication.rb | 56 ++++++++++--------- app/controllers/password_resets_controller.rb | 6 +- app/controllers/sessions_controller.rb | 1 + app/models/user.rb | 9 ++- .../20240703041238_add_index_to_user_email.rb | 5 -- db/schema.rb | 2 +- db/seeds.rb | 4 +- spec/factories/users.rb | 1 + spec/models/user_spec.rb | 1 + 10 files changed, 44 insertions(+), 43 deletions(-) delete mode 100644 db/migrate/20240703041238_add_index_to_user_email.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 658bada1..361611fe 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,3 @@ class ApplicationController < ActionController::Base include Authentication - - before_action :authenticate_user! end diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 1bb4e35a..55400474 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -4,31 +4,35 @@ module Authentication included do helper_method :current_user, :user_signed_in? - def authenticate_user! - redirect_to root_path, alert: t("controllers.concerns.authentication.unauthorized") unless user_signed_in? - end - - def current_user - Current.user ||= authenticate_user_from_session - end - - def authenticate_user_from_session - User.find_by(id: session[:user_id]) - end - - def user_signed_in? - current_user.present? - end - - def login(user) - Current.user = user - reset_session - session[:user_id] = user.id - end - - def logout - Current.user = nil - reset_session - end + before_action :authenticate_user! + end + + def authenticate_user! + return current_user if user_signed_in? + + redirect_to root_path, alert: t("controllers.concerns.authentication.unauthorized") + end + + def current_user + Current.user ||= authenticate_user_from_session + end + + def authenticate_user_from_session + User.find_by(id: session[:user_id]) + end + + def user_signed_in? + current_user.present? + end + + def login(user) + Current.user = user + reset_session + session[:user_id] = user.id + end + + def logout + Current.user = nil + reset_session end end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index b79929ac..15f94e59 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -1,5 +1,6 @@ class PasswordResetsController < ApplicationController skip_before_action :authenticate_user!, only: [:new, :create] + before_action :set_user_by_token, only: [:edit, :update] def new @@ -15,7 +16,7 @@ def create PasswordMailer.with( user: @user, token: @user.generate_token_for(:password_reset) - ).password_reset.deliver_now + ).password_reset.deliver_later end redirect_to root_path, notice: t("controllers.password_resets.create.notice") @@ -33,8 +34,9 @@ def update def set_user_by_token @user = User.find_by_token_for(:password_reset, params[:token]) + return if @user.present? - redirect_to new_password_reset_path alert: t("controllers.password_resets.errors.invalid_token") if @user.blank? + redirect_to new_password_reset_path alert: t("controllers.password_resets.errors.invalid_token") end def password_params diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index ad5454d5..00e79feb 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -2,6 +2,7 @@ class SessionsController < ApplicationController skip_before_action :authenticate_user! def new + @user = User.new end def create diff --git a/app/models/user.rb b/app/models/user.rb index d37580ee..86e22a32 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,6 +6,7 @@ # email :string not null # in_app_notifications_enabled :boolean default(TRUE), not null # mail_notifications_enabled :boolean default(TRUE), not null +# password_digest :string not null # role :string # created_at :datetime not null # updated_at :datetime not null @@ -15,10 +16,12 @@ # index_users_on_email (email) UNIQUE # class User < ApplicationRecord - PASSWORD_RESET_EXPIRATION = 15.minutes + PASSWORD_RESET_EXPIRATION = 60.minutes normalizes :email, with: ->(email) { email.strip.downcase } + has_secure_password + has_one :profile, as: :profileable, dependent: :destroy has_and_belongs_to_many :events @@ -26,10 +29,6 @@ class User < ApplicationRecord validates :email, presence: true, uniqueness: true validates :password_digest, presence: true - normalizes :email, with: ->(email) { email.strip.downcase } - - has_secure_password - generates_token_for :password_reset, expires_in: PASSWORD_RESET_EXPIRATION do password_salt&.last(10) end diff --git a/db/migrate/20240703041238_add_index_to_user_email.rb b/db/migrate/20240703041238_add_index_to_user_email.rb deleted file mode 100644 index ff03658d..00000000 --- a/db/migrate/20240703041238_add_index_to_user_email.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIndexToUserEmail < ActiveRecord::Migration[7.1] - def change - add_index :users, :email, unique: true - end -end diff --git a/db/schema.rb b/db/schema.rb index a6e60d62..d7c97655 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_07_03_041238) do +ActiveRecord::Schema[7.1].define(version: 2024_06_28_211903) do create_table "conferences", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", null: false diff --git a/db/seeds.rb b/db/seeds.rb index ab538a33..cee8acc9 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,7 @@ -conference = Conference.create!(name: "RailsWorld 2024") +conference = Conference.find_or_create_by!(name: "RailsWorld 2024") # Users -user = User.create!(email: "dev@example.com") +user = User.create!(email: "dev@example.com", password: "foobar", password_confirmation: "foobar") # Tags Tag.create!(name: "Hotwire") diff --git a/spec/factories/users.rb b/spec/factories/users.rb index bebbe6aa..14539a09 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -6,6 +6,7 @@ # email :string not null # in_app_notifications_enabled :boolean default(TRUE), not null # mail_notifications_enabled :boolean default(TRUE), not null +# password_digest :string not null # role :string # created_at :datetime not null # updated_at :datetime not null diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2b251366..db4430e7 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6,6 +6,7 @@ # email :string not null # in_app_notifications_enabled :boolean default(TRUE), not null # mail_notifications_enabled :boolean default(TRUE), not null +# password_digest :string not null # role :string # created_at :datetime not null # updated_at :datetime not null From cb97d76c3091256462c1fc6d921c6bf01b778562 Mon Sep 17 00:00:00 2001 From: Sergio-e <33036058+Sergio-e@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:50:01 -0600 Subject: [PATCH 2/2] Authentication improvements --- .env.template | 1 + .../default.md => PULL_REQUEST_TEMPLATE.md} | 0 .github/workflows/github-actions.yml | 2 + app/controllers/concerns/authentication.rb | 30 ++++++----- app/controllers/main_controller.rb | 3 +- app/controllers/password_resets_controller.rb | 6 +-- app/controllers/passwords_controller.rb | 2 +- app/controllers/registrations_controller.rb | 2 +- app/controllers/sessions_controller.rb | 5 +- app/models/user.rb | 1 + app/views/passwords/edit.html.erb | 2 +- app/views/registrations/new.html.erb | 8 +-- app/views/sessions/new.html.erb | 6 +-- config/locales/en.yml | 37 ++----------- db/seeds.rb | 2 +- .../password_resets_controller_spec.rb | 17 +++--- spec/controllers/passwords_controller_spec.rb | 3 +- .../registrations_controller_spec.rb | 1 - spec/controllers/sessions_controller_spec.rb | 5 +- spec/factories/users.rb | 2 +- spec/rails_helper.rb | 3 +- spec/support/authentication_helper.rb | 17 ++++++ spec/system/test_spec.rb | 8 --- spec/system/user_sign_in_spec.rb | 37 +++++++++++++ spec/system/user_sign_up_spec.rb | 52 +++++++++++++++++++ 25 files changed, 164 insertions(+), 88 deletions(-) rename .github/{PULL_REQUEST_TEMPLATE/default.md => PULL_REQUEST_TEMPLATE.md} (100%) create mode 100644 spec/support/authentication_helper.rb delete mode 100644 spec/system/test_spec.rb create mode 100644 spec/system/user_sign_in_spec.rb create mode 100644 spec/system/user_sign_up_spec.rb diff --git a/.env.template b/.env.template index c80d0eff..0e694f27 100644 --- a/.env.template +++ b/.env.template @@ -1,5 +1,6 @@ OVERMIND_PROCFILE=Procfile.dev HIVEMIND_PROCFILE=Procfile.dev +PORT=3000 OVERMIND_ENV=./.env.local HIVEMIND_ENV=./.env.local RSPEC_RETRY_RETRY_COUNT=0 diff --git a/.github/PULL_REQUEST_TEMPLATE/default.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE/default.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 4ed4df5d..e955b403 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -86,6 +86,8 @@ jobs: - name: Setup DB run: | bundle exec rails db:test:prepare + - name: Assets Precompile + run: bundle exec rails assets:precompile - name: Run tests run: | bundle exec rspec diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 55400474..6414e533 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -4,25 +4,31 @@ module Authentication included do helper_method :current_user, :user_signed_in? - before_action :authenticate_user! + before_action :authenticate_user, :authenticate_user! end - def authenticate_user! - return current_user if user_signed_in? - - redirect_to root_path, alert: t("controllers.concerns.authentication.unauthorized") + class_methods do + def allow_unauthenticated_access(**options) + skip_before_action :authenticate_user!, **options + end end - def current_user - Current.user ||= authenticate_user_from_session - end + private + + def current_user = Current.user - def authenticate_user_from_session - User.find_by(id: session[:user_id]) + def user_signed_in? = Current.user.present? + + def authenticate_user + Current.user = User.find_by(id: session[:user_id]) end - def user_signed_in? - current_user.present? + def authenticate_user! + authenticate_user + + if !user_signed_in? + redirect_to new_session_path, alert: t("controllers.concerns.authentication.unauthorized") + end end def login(user) diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index f6b17f11..c98a8eaa 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -1,5 +1,6 @@ class MainController < ApplicationController - skip_before_action :authenticate_user! + allow_unauthenticated_access + def index end end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index 15f94e59..8e4429f4 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -1,5 +1,5 @@ class PasswordResetsController < ApplicationController - skip_before_action :authenticate_user!, only: [:new, :create] + allow_unauthenticated_access before_action :set_user_by_token, only: [:edit, :update] @@ -19,7 +19,7 @@ def create ).password_reset.deliver_later end - redirect_to root_path, notice: t("controllers.password_resets.create.notice") + redirect_to new_session_path, notice: t("controllers.password_resets.create.notice") end def update @@ -36,7 +36,7 @@ def set_user_by_token @user = User.find_by_token_for(:password_reset, params[:token]) return if @user.present? - redirect_to new_password_reset_path alert: t("controllers.password_resets.errors.invalid_token") + redirect_to new_password_reset_path, alert: t("controllers.password_resets.errors.invalid_token") end def password_params diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 82969ec9..1930c97b 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -3,7 +3,7 @@ def edit end def update - if current_user.update(password_params) + if Current.user.update(password_params) redirect_to edit_password_path, notice: t("controllers.passwords.update.notice") else render :edit, status: :unprocessable_entity diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 199ba626..37651612 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,5 +1,5 @@ class RegistrationsController < ApplicationController - skip_before_action :authenticate_user! + allow_unauthenticated_access def new @user = User.new diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 00e79feb..fae2553e 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,5 @@ class SessionsController < ApplicationController - skip_before_action :authenticate_user! + allow_unauthenticated_access only: [:new, :create] def new @user = User.new @@ -12,8 +12,7 @@ def create login @user redirect_to root_path, notice: t("controllers.sessions.create.notice") else - flash[:alert] = t("controllers.sessions.create.alert") - render :new, status: :unprocessable_entity + redirect_to new_session_path, alert: t("controllers.sessions.create.alert") end end diff --git a/app/models/user.rb b/app/models/user.rb index 86e22a32..8b641a70 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -28,6 +28,7 @@ class User < ApplicationRecord validates :email, presence: true, uniqueness: true validates :password_digest, presence: true + validates :password, length: {minimum: 8}, if: -> { password.present? } generates_token_for :password_reset, expires_in: PASSWORD_RESET_EXPIRATION do password_salt&.last(10) diff --git a/app/views/passwords/edit.html.erb b/app/views/passwords/edit.html.erb index 7a30272d..b1959002 100644 --- a/app/views/passwords/edit.html.erb +++ b/app/views/passwords/edit.html.erb @@ -1,6 +1,6 @@

Update Password

-<%= form_with model: current_user, url: password_path do |form| %> +<%= form_with model: Current.user, url: password_path do |form| %> <% if form.object.errors.any? %> <% form.object.errors.full_messages.each do |message| %>
<%= message %>
diff --git a/app/views/registrations/new.html.erb b/app/views/registrations/new.html.erb index 82160074..f1a6c724 100644 --- a/app/views/registrations/new.html.erb +++ b/app/views/registrations/new.html.erb @@ -9,20 +9,20 @@
<%= form.label :email %> - <%= form.email_field :email %> + <%= form.email_field :email, data: { test_id: "email_field" } %>
<%= form.label :password %> - <%= form.password_field :password %> + <%= form.password_field :password, data: { test_id: "password_field" } %>
<%= form.label :password_confirmation %> - <%= form.password_field :password_confirmation %> + <%= form.password_field :password_confirmation, data: { test_id: "password_confirmation_field" } %>
- <%= form.submit "Sign Up" %> + <%= form.submit "Sign Up", data: { test_id: "sign_up_button" } %>
<% end %> diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb index 426a07a5..ef79ab62 100644 --- a/app/views/sessions/new.html.erb +++ b/app/views/sessions/new.html.erb @@ -3,16 +3,16 @@ <%= form_with model: @user, url: session_path do |form| %>
<%= form.label :email %> - <%= form.email_field :email %> + <%= form.email_field :email, data: { test_id: "email_field" } %>
<%= form.label :password %> - <%= form.password_field :password %> + <%= form.password_field :password, data: { test_id: "password_field" } %>
- <%= form.submit "Log in" %> + <%= form.submit "Log in", data: { test_id: "sign_in_button" } %>
<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index e18f993c..297afaf0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,50 +1,21 @@ -# Files in the config/locales directory are used for internationalization and -# are automatically loaded by Rails. If you want to use locales other than -# English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t "hello" -# -# In views, this is aliased to just `t`: -# -# <%= t("hello") %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# To learn more about the API, please read the Rails Internationalization guide -# at https://guides.rubyonrails.org/i18n.html. -# -# Be aware that YAML interprets the following case-insensitive strings as -# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings -# must be quoted to be interpreted as strings. For example: -# -# en: -# "yes": yup -# enabled: "ON" - en: controllers: concerns: authentication: - unauthorized: "You must be logged in to do that." + unauthorized: "You need to sign in or sign up before continuing." password_resets: create: notice: "Check your email to reset your password." update: notice: "Your password has been reset successfully. Please login." errors: - invalid_token: "Invalid token, please try again" + invalid_token: "Invalid token, please try again." passwords: update: notice: "Your password has been updated successfully." sessions: create: - notice: "You have signed successfully." + notice: "Signed in successfully." alert: "Invalid email or password." destroy: - notice: "You have been logged out." \ No newline at end of file + notice: "You have been logged out." diff --git a/db/seeds.rb b/db/seeds.rb index cee8acc9..16de4ebb 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,7 @@ conference = Conference.find_or_create_by!(name: "RailsWorld 2024") # Users -user = User.create!(email: "dev@example.com", password: "foobar", password_confirmation: "foobar") +user = User.create!(email: "dev@example.com", password: "foobar2024", password_confirmation: "foobar2024") # Tags Tag.create!(name: "Hotwire") diff --git a/spec/controllers/password_resets_controller_spec.rb b/spec/controllers/password_resets_controller_spec.rb index bb2ec1c6..bd7dfe3a 100644 --- a/spec/controllers/password_resets_controller_spec.rb +++ b/spec/controllers/password_resets_controller_spec.rb @@ -22,17 +22,17 @@ end it "sends a password reset email" do - expect { post :create, params: params }.to change { ActionMailer::Base.deliveries.count }.by(1) - expect(response).to redirect_to(root_path) + expect { + post :create, params: params + }.to have_enqueued_mail(PasswordMailer, :password_reset) + expect(response).to redirect_to(new_session_path) end end end describe "GET #edit" do - let(:current) { instance_double(Current) } - before do - allow(Current).to receive(:user).and_return(user) + sign_in(user) end context "with valid token" do @@ -46,17 +46,16 @@ context "with invalid token" do it "redirects to new password reset path" do get :edit, params: {token: "invalid_token"} - expect(response).to redirect_to(new_password_reset_path(alert: I18n.t("controllers.password_resets.errors.invalid_token"))) + expect(response).to redirect_to(new_password_reset_path) end end end describe "PUT #update" do let(:new_password) { "new_password" } - let(:current) { instance_double(Current) } before do - allow(Current).to receive(:user).and_return(user) + sign_in(user) end context "with valid params" do @@ -108,7 +107,7 @@ it "does not update the user password" do expect { put :update, params: params }.not_to change { user.reload.password_digest } - expect(response).to redirect_to(new_password_reset_path(alert: I18n.t("controllers.password_resets.errors.invalid_token"))) + expect(response).to redirect_to(new_password_reset_path) end end end diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb index 9ad546b1..e3b29a21 100644 --- a/spec/controllers/passwords_controller_spec.rb +++ b/spec/controllers/passwords_controller_spec.rb @@ -4,10 +4,9 @@ RSpec.describe PasswordsController, type: :controller do let!(:user) { create(:user, password: "password") } - let(:current) { instance_double(Current) } before do - allow(Current).to receive(:user).and_return(user) + sign_in(user) end describe "GET #edit" do diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index fa0f0318..c2556edd 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -7,7 +7,6 @@ it "returns a success response" do get :new expect(response).to have_http_status(:ok) - assigns(:user).should be_a_new(User) end end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 31e8b614..55cb7628 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -27,7 +27,6 @@ post :create, params: params expect(session[:user_id]).to eq(user.id) expect(response).to redirect_to(root_path) - assigns(:user).should eq(user) end end @@ -43,8 +42,8 @@ it "does not create a User session" do expect { post :create, params: params }.not_to change(User, :count) - expect(response).to have_http_status(:unprocessable_content) - expect(response).to render_template(:new) + expect(response).to have_http_status(:found) + expect(response).to redirect_to(new_session_path) end end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 14539a09..7f25f9e4 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -18,7 +18,7 @@ FactoryBot.define do factory :user do email { Faker::Internet.email } - password { "password" } + password { "password2024" } trait :with_profile do profile diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 366e19e9..b23e95a3 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -30,6 +30,8 @@ abort e.to_s.strip end RSpec.configure do |config| + ActiveJob::Base.queue_adapter = :test + config.include FactoryBot::Syntax::Methods # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false @@ -58,5 +60,4 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") - config.include FactoryBot::Syntax::Methods end diff --git a/spec/support/authentication_helper.rb b/spec/support/authentication_helper.rb new file mode 100644 index 00000000..ed2091c2 --- /dev/null +++ b/spec/support/authentication_helper.rb @@ -0,0 +1,17 @@ +module AuthenticationHelper + def sign_in(user, password = "password2024") + if respond_to?(:visit) # System specs + visit new_session_path + find_dti("email_field").set(user.email) + find_dti("password_field").set(password) + find_dti("sign_in_button").click + else # Controller specs + session[:user_id] = user.id + end + end +end + +RSpec.configure do |config| + config.include AuthenticationHelper, type: :system + config.include AuthenticationHelper, type: :controller +end diff --git a/spec/system/test_spec.rb b/spec/system/test_spec.rb deleted file mode 100644 index 2510aef5..00000000 --- a/spec/system/test_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require "rails_helper" - -RSpec.describe "System specs health check", type: :system do - it "website is up" do - visit "/up" - expect(page).to have_http_status(:ok) - end -end diff --git a/spec/system/user_sign_in_spec.rb b/spec/system/user_sign_in_spec.rb new file mode 100644 index 00000000..62f87f69 --- /dev/null +++ b/spec/system/user_sign_in_spec.rb @@ -0,0 +1,37 @@ +require "rails_helper" + +RSpec.describe "User sign in", type: :system do + let!(:user) { create(:user, email: "test@test.com", password: "foobar2024") } + + it "redirects to the login page when trying to access another page" do + visit edit_password_path + expect(page).to have_current_path(new_session_path) + expect(page).to have_content("You need to sign in or sign up before continuing.") + end + + context "when the user inputs invalid credentials" do + it "does not sign the user in" do + visit new_session_path + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("wrongpassword") + find_dti("sign_in_button").click + expect(page).to have_content("Invalid email or password.") + + visit edit_password_path + expect(page).to have_current_path(new_session_path) + end + end + + context "when the user inputs valid credentials" do + it "signs the user in" do + visit new_session_path + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("foobar2024") + find_dti("sign_in_button").click + expect(page).to have_content("Signed in successfully.") + + visit edit_password_path + expect(page).to have_current_path(edit_password_path) + end + end +end diff --git a/spec/system/user_sign_up_spec.rb b/spec/system/user_sign_up_spec.rb new file mode 100644 index 00000000..8d3915fe --- /dev/null +++ b/spec/system/user_sign_up_spec.rb @@ -0,0 +1,52 @@ +require "rails_helper" + +RSpec.describe "User sign up", type: :system do + before { visit new_registration_path } + + context "when the password & password confirmation doesn't match up" do + it "does not create a new user" do + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("hello") + find_dti("password_confirmation_field").set("world") + find_dti("sign_up_button").click + + expect(page).to have_content("Password confirmation doesn't match Password") + end + end + + context "when the email is already taken" do + let!(:user) { create(:user, email: "test@test.com") } + + it "does not create a new user" do + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("foobar2024") + find_dti("password_confirmation_field").set("foobar2024") + find_dti("sign_up_button").click + + expect(page).to have_content("Email has already been taken") + end + end + + context "when the password doesn't met the length criteria" do + it "does not create a new user" do + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("foobar") + find_dti("password_confirmation_field").set("foobar") + find_dti("sign_up_button").click + + expect(page).to have_content("Password is too short (minimum is 8 characters)") + end + end + + context "when the user inputs valid credentials" do + it "creates a new user" do + find_dti("email_field").set("test@test.com") + find_dti("password_field").set("foobar2024") + find_dti("password_confirmation_field").set("foobar2024") + find_dti("sign_up_button").click + + visit edit_password_path + expect(page).to have_current_path(edit_password_path) + end + end +end