require "spec_helper" describe Clearance::PasswordsController do it { is_expected.to be_a Clearance::BaseController } describe "#new" do it "renders the password reset form" do get :new expect(response).to be_successful expect(response).to render_template(:new) end end describe "#create" do context "email corresponds to an existing user" do it "generates a password change token" do user = create(:user) post :create, params: { password: { email: user.email.upcase }, } expect(user.reload.confirmation_token).not_to be_nil end it "sends the password reset email" do ActionMailer::Base.deliveries.clear user = create(:user) post :create, params: { password: { email: user.email }, } email = ActionMailer::Base.deliveries.last expect(email.subject).to match(translated_string("passwords.edit.title")) end it "re-renders the page when turbo is enabled" do user = create(:user) post :create, params: { password: { email: user.email.upcase }, } expect(response).to have_http_status(:accepted) end end context "email param is missing" do it "displays flash error on new page" do post :create, params: { password: {}, } expect(flash.now[:alert]).to match(translated_string("flashes.failure_when_missing_email")) expect(response).to render_template(:new) end it "re-renders the page when turbo is enabled" do post :create, params: { password: {}, } expect(response).to have_http_status(:unprocessable_entity) end end context "email param is blank" do it "displays flash error on new page" do post :create, params: { password: { email: "", }, } expect(flash.now[:alert]).to match(translated_string("flashes.failure_when_missing_email")) expect(response).to render_template(:new) end it "re-renders the page when turbo is enabled" do post :create, params: { password: { email: "", }, } expect(response).to have_http_status(:unprocessable_entity) end end context "email does not belong to an existing user" do it "does not deliver an email" do ActionMailer::Base.deliveries.clear email = "this_user_does_not_exist@non_existent_domain.com" post :create, params: { password: { email: email }, } expect(ActionMailer::Base.deliveries).to be_empty end it "still responds with error so as not to leak registered users" do email = "this_user_does_not_exist@non_existent_domain.com" post :create, params: { password: { email: email }, } expect(response).to be_successful expect(response).to render_template "passwords/create" end it "has the same status code as a successful request" do email = "this_user_does_not_exist@non_existent_domain.com" post :create, params: { password: { email: email }, } expect(response).to have_http_status(:accepted) end end end describe "#edit" do context "valid id and token are supplied in url" do it "redirects to the edit page with token now removed from url" do user = create(:user, :with_forgotten_password) get :edit, params: { user_id: user, token: user.confirmation_token, } expect(response).to be_redirect expect(response).to have_http_status(:found) expect(response).to redirect_to edit_user_password_url(user) expect(session[:password_reset_token]).to eq user.confirmation_token end end context "valid id in url and valid token in session" do it "renders the password reset form" do user = create(:user, :with_forgotten_password) request.session[:password_reset_token] = user.confirmation_token get :edit, params: { user_id: user, } expect(response).to be_successful expect(response).to render_template(:edit) expect(assigns(:user)).to eq user end end context "blank token is supplied" do it "renders the new password reset form with a flash alert" do get :edit, params: { user_id: 1, token: "", } expect(response).to render_template(:new) expect(flash.now[:alert]).to match(translated_string("flashes.failure_when_forbidden")) end end context "invalid token is supplied" do it "renders the new password reset form with a flash alert" do user = create(:user, :with_forgotten_password) get :edit, params: { user_id: 1, token: user.confirmation_token + "a", } expect(response).to render_template(:new) expect(flash.now[:alert]).to match(translated_string("flashes.failure_when_forbidden")) end end context "old token in session and recent token in params" do it "updates password reset session and redirect to edit page" do user = create(:user, :with_forgotten_password) request.session[:password_reset_token] = user.confirmation_token user.forgot_password! get :edit, params: { user_id: user.id, token: user.reload.confirmation_token, } expect(response).to redirect_to(edit_user_password_url(user)) expect(session[:password_reset_token]).to eq(user.confirmation_token) end end end describe "#update" do context "valid id, token, and new password provided" do it "updates the user's password" do user = create(:user, :with_forgotten_password) old_encrypted_password = user.encrypted_password put :update, params: update_parameters( user, new_password: "my_new_password", ) expect(user.reload.encrypted_password).not_to eq old_encrypted_password expect(response).to have_http_status(:see_other) end it "signs in the user" do user = create(:user, :with_forgotten_password) put :update, params: update_parameters( user, new_password: "my_new_password", ) expect(current_user).to eq(user) end context "when Clearance is configured to not sign in the user" do it "doesn't sign in the user" do Clearance.configure do |config| config.sign_in_on_password_reset = false end user = create(:user, :with_forgotten_password) put :update, params: update_parameters( user, new_password: "my_new_password", ) expect(current_user).to be_nil end end end context "password update fails" do it "does not update the password" do user = create(:user, :with_forgotten_password) old_encrypted_password = user.encrypted_password put :update, params: update_parameters( user, new_password: "", ) user.reload expect(user.encrypted_password).to eq old_encrypted_password expect(user.confirmation_token).to be_present end it "does not raise NoMethodError from incomplete password_reset params" do user = create(:user, :with_forgotten_password) expect do put :update, params: { user_id: user, token: user.confirmation_token, password_reset: {}, } end.not_to raise_error end it "re-renders the password edit form" do user = create(:user, :with_forgotten_password) put :update, params: update_parameters( user, new_password: "", ) expect(flash.now[:alert]).to match(translated_string("flashes.failure_after_update")) expect(response).to have_http_status(:unprocessable_entity) expect(response).to render_template(:edit) end it "doesn't sign in the user" do user = create(:user, :with_forgotten_password) put :update, params: update_parameters( user, new_password: "", ) expect(current_user).to be_nil end end end def update_parameters(user, options = {}) new_password = options.fetch(:new_password) { user_id: user, token: user.confirmation_token, password_reset: { password: new_password } } end def current_user request.env[:clearance].current_user end end