From dc9c99693b031f218ff798da78cb7a82fa1d5fdb Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Tue, 4 Feb 2020 22:46:23 +0100 Subject: [PATCH 1/5] Add frontend controllers and views for user account management. Needs to be enabled via # config/initializers/alchemy.rb Alchemy::Devise.enable_user_accounts = true --- .../alchemy/accounts_controller.rb | 18 +++ .../alchemy/confirmations_controller.rb | 11 ++ .../alchemy/passwords_controller.rb | 11 ++ .../alchemy/sessions_controller.rb | 11 ++ app/mailers/alchemy/notifications.rb | 20 ++- app/models/alchemy/user.rb | 9 ++ app/views/alchemy/accounts/show.html.erb | 3 + .../alchemy/devise/shared/_links.html.erb | 15 ++ .../confirmation_instructions.de.text.erb | 5 + .../confirmation_instructions.en.text.erb | 5 + ...er_reset_password_instructions.de.text.erb | 8 ++ ...er_reset_password_instructions.en.text.erb | 8 ++ ...er_reset_password_instructions.es.text.erb | 8 ++ ...er_reset_password_instructions.ru.text.erb | 8 ++ app/views/alchemy/passwords/edit.html.erb | 27 ++++ app/views/alchemy/passwords/new.html.erb | 18 +++ app/views/alchemy/sessions/new.html.erb | 32 +++++ config/routes.rb | 32 +++++ lib/alchemy/devise.rb | 23 ++- spec/controllers/accounts_controller_spec.rb | 45 ++++++ .../confirmations_controller_spec.rb | 46 ++++++ spec/controllers/passwords_controller_spec.rb | 34 +++++ spec/controllers/sessions_controller_spec.rb | 64 +++++++++ spec/mailers/notifications_spec.rb | 38 +++++ spec/routing/account_routing_spec.rb | 132 ++++++++++++++++++ spec/routing/admin_password_routing_spec.rb | 43 ++++++ .../admin_user_session_routing_spec.rb | 32 +++++ spec/routing/password_routing_spec.rb | 114 ++++++++++----- spec/routing/session_routing_spec.rb | 87 ++++++++---- 29 files changed, 846 insertions(+), 61 deletions(-) create mode 100644 app/controllers/alchemy/accounts_controller.rb create mode 100644 app/controllers/alchemy/confirmations_controller.rb create mode 100644 app/controllers/alchemy/passwords_controller.rb create mode 100644 app/controllers/alchemy/sessions_controller.rb create mode 100644 app/views/alchemy/accounts/show.html.erb create mode 100644 app/views/alchemy/devise/shared/_links.html.erb create mode 100644 app/views/alchemy/notifications/confirmation_instructions.de.text.erb create mode 100644 app/views/alchemy/notifications/confirmation_instructions.en.text.erb create mode 100644 app/views/alchemy/notifications/member_reset_password_instructions.de.text.erb create mode 100644 app/views/alchemy/notifications/member_reset_password_instructions.en.text.erb create mode 100644 app/views/alchemy/notifications/member_reset_password_instructions.es.text.erb create mode 100644 app/views/alchemy/notifications/member_reset_password_instructions.ru.text.erb create mode 100644 app/views/alchemy/passwords/edit.html.erb create mode 100644 app/views/alchemy/passwords/new.html.erb create mode 100644 app/views/alchemy/sessions/new.html.erb create mode 100644 spec/controllers/accounts_controller_spec.rb create mode 100644 spec/controllers/confirmations_controller_spec.rb create mode 100644 spec/controllers/passwords_controller_spec.rb create mode 100644 spec/controllers/sessions_controller_spec.rb create mode 100644 spec/routing/account_routing_spec.rb create mode 100644 spec/routing/admin_password_routing_spec.rb create mode 100644 spec/routing/admin_user_session_routing_spec.rb diff --git a/app/controllers/alchemy/accounts_controller.rb b/app/controllers/alchemy/accounts_controller.rb new file mode 100644 index 0000000..13bf743 --- /dev/null +++ b/app/controllers/alchemy/accounts_controller.rb @@ -0,0 +1,18 @@ +module Alchemy + class AccountsController < ::Devise::RegistrationsController + helper 'Alchemy::Pages' + + def show + authorize! :show, current_alchemy_user + @user = current_alchemy_user + end + + private + + def permission_denied(*) + store_location_for(:user, account_path) + flash[:warning] = t(:unauthenticated, scope: 'devise.failure') + redirect_to alchemy.login_path + end + end +end diff --git a/app/controllers/alchemy/confirmations_controller.rb b/app/controllers/alchemy/confirmations_controller.rb new file mode 100644 index 0000000..daefa26 --- /dev/null +++ b/app/controllers/alchemy/confirmations_controller.rb @@ -0,0 +1,11 @@ +module Alchemy + class ConfirmationsController < ::Devise::ConfirmationsController + helper 'Alchemy::Pages' + + private + + def new_session_path(*) + alchemy.login_path + end + end +end diff --git a/app/controllers/alchemy/passwords_controller.rb b/app/controllers/alchemy/passwords_controller.rb new file mode 100644 index 0000000..52f87bb --- /dev/null +++ b/app/controllers/alchemy/passwords_controller.rb @@ -0,0 +1,11 @@ +module Alchemy + class PasswordsController < ::Devise::PasswordsController + helper 'Alchemy::Pages' + + private + + def new_session_path(*) + alchemy.login_path + end + end +end diff --git a/app/controllers/alchemy/sessions_controller.rb b/app/controllers/alchemy/sessions_controller.rb new file mode 100644 index 0000000..d119f16 --- /dev/null +++ b/app/controllers/alchemy/sessions_controller.rb @@ -0,0 +1,11 @@ +module Alchemy + class SessionsController < ::Devise::SessionsController + helper 'Alchemy::Pages' + + private + + def after_sign_in_path_for(user) + stored_location_for(user) || alchemy.account_path + end + end +end diff --git a/app/mailers/alchemy/notifications.rb b/app/mailers/alchemy/notifications.rb index 33327df..cd27825 100644 --- a/app/mailers/alchemy/notifications.rb +++ b/app/mailers/alchemy/notifications.rb @@ -1,5 +1,5 @@ module Alchemy - class Notifications < ActionMailer::Base + class Notifications < ::Devise::Mailer default(from: Config.get(:mailer)['mail_from']) @@ -21,6 +21,15 @@ def alchemy_user_created(user) ) end + def member_reset_password_instructions(user, token, opts={}) + @user = user + @token = token + mail( + to: user.email, + subject: Alchemy.t("Reset password instructions") + ) + end + def reset_password_instructions(user, token, opts={}) @user = user @token = token @@ -29,5 +38,14 @@ def reset_password_instructions(user, token, opts={}) subject: Alchemy.t("Reset password instructions") ) end + + def confirmation_instructions(user, token, opts={}) + @user = user + @token = token + mail( + to: user.email, + subject: Alchemy.t("Account confirmation instructions") + ) + end end end diff --git a/app/models/alchemy/user.rb b/app/models/alchemy/user.rb index bd574c8..7c042fe 100644 --- a/app/models/alchemy/user.rb +++ b/app/models/alchemy/user.rb @@ -157,6 +157,15 @@ def deliver_welcome_mail end end + # Overwritten to send a different email to members + def send_reset_password_instructions_notification(token) + if has_role?('member') + send_devise_notification(:member_reset_password_instructions, token, {}) + else + send_devise_notification(:reset_password_instructions, token, {}) + end + end + private def logged_in_timeout diff --git a/app/views/alchemy/accounts/show.html.erb b/app/views/alchemy/accounts/show.html.erb new file mode 100644 index 0000000..a5c0a17 --- /dev/null +++ b/app/views/alchemy/accounts/show.html.erb @@ -0,0 +1,3 @@ +

Hallo <%= @user.fullname %>

+ +<%= link_to 'Edit account', alchemy.edit_account_path %> diff --git a/app/views/alchemy/devise/shared/_links.html.erb b/app/views/alchemy/devise/shared/_links.html.erb new file mode 100644 index 0000000..cd17113 --- /dev/null +++ b/app/views/alchemy/devise/shared/_links.html.erb @@ -0,0 +1,15 @@ +<%- if controller_name != 'sessions' %> + <%= link_to t('.login', default: 'Log in'), alchemy.login_path %>
+<% end %> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to t('.sign_up', default: 'Sign up'), alchemy.new_account_path %>
+<% end %> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> + <%= link_to t('.forgot_password', default: 'Forgot your password?'), alchemy.new_password_path %>
+<% end %> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to t('.confirmation_instructions', default: "Didn't receive confirmation instructions?"), alchemy.new_confirmation_path %>
+<% end %> diff --git a/app/views/alchemy/notifications/confirmation_instructions.de.text.erb b/app/views/alchemy/notifications/confirmation_instructions.de.text.erb new file mode 100644 index 0000000..d96c9e1 --- /dev/null +++ b/app/views/alchemy/notifications/confirmation_instructions.de.text.erb @@ -0,0 +1,5 @@ +Willkommen <%= @user.fullname %>! + +Bitte bestätigen Sie Ihre E-Mail-Adresse durch klicken auf folgenden Link: + +<%= alchemy.confirmation_url(@user, confirmation_token: @token) %> diff --git a/app/views/alchemy/notifications/confirmation_instructions.en.text.erb b/app/views/alchemy/notifications/confirmation_instructions.en.text.erb new file mode 100644 index 0000000..1aacf31 --- /dev/null +++ b/app/views/alchemy/notifications/confirmation_instructions.en.text.erb @@ -0,0 +1,5 @@ +Welcome <%= @user.fullname %>! + +You can confirm your account email through the link below: + +<%= alchemy.confirmation_url(@user, confirmation_token: @token) %> diff --git a/app/views/alchemy/notifications/member_reset_password_instructions.de.text.erb b/app/views/alchemy/notifications/member_reset_password_instructions.de.text.erb new file mode 100644 index 0000000..f306fa0 --- /dev/null +++ b/app/views/alchemy/notifications/member_reset_password_instructions.de.text.erb @@ -0,0 +1,8 @@ +Hallo <%= @user.fullname %>. + +Sie haben angefordert Ihr Passwort zurückzusetzen. Dies kann durch anklicken des nachfolgenden Links bestätigt werden. + +<%= alchemy.edit_password_url(@user, reset_password_token: @token) %> + +Wenn Sie diese Zurücksetzung nicht angefragt haben, dann können Sie diese E-Mail einfach ignorieren. +Ihr Passwort wird erst dann zurückgesetzt, wenn Sie den Link anklicken. diff --git a/app/views/alchemy/notifications/member_reset_password_instructions.en.text.erb b/app/views/alchemy/notifications/member_reset_password_instructions.en.text.erb new file mode 100644 index 0000000..e1150cb --- /dev/null +++ b/app/views/alchemy/notifications/member_reset_password_instructions.en.text.erb @@ -0,0 +1,8 @@ +Hello <%= @user.name %>. + +You have requested to change your password. Please confirm this by clicking the link below. + +<%= alchemy.edit_password_url(@user, reset_password_token: @token) %> + +If you didn't request this, please ignore this email. +Your password won't change until you access the link above and create a new one. diff --git a/app/views/alchemy/notifications/member_reset_password_instructions.es.text.erb b/app/views/alchemy/notifications/member_reset_password_instructions.es.text.erb new file mode 100644 index 0000000..55d62af --- /dev/null +++ b/app/views/alchemy/notifications/member_reset_password_instructions.es.text.erb @@ -0,0 +1,8 @@ +Hola <%= @user.name %>. + +Has solicitado modificar tu contraseña. Por favor, confírmalo pulsando en el siguiente enlace. + +<%= alchemy.edit_password_url(@user, reset_password_token: @token) %> + +Si no has sido tu el que ha hecho la solicitud, ignora este correo. +Tu contraseña no cambiará hasta que no accedas al enlace de arriba y generes una nueva. diff --git a/app/views/alchemy/notifications/member_reset_password_instructions.ru.text.erb b/app/views/alchemy/notifications/member_reset_password_instructions.ru.text.erb new file mode 100644 index 0000000..c4ecfdc --- /dev/null +++ b/app/views/alchemy/notifications/member_reset_password_instructions.ru.text.erb @@ -0,0 +1,8 @@ +Здравствуйте, <%= @user.name %>. + +Вы сделали запрос на смену пароля. Пожалуйста подтвердите это, нажав на ссылку ниже. + +<%= alchemy.edit_password_url(@user, reset_password_token: @token) %> + +Если вы не делали запрос, просто проигнорируйте это письмо. +Ваш пароль не изменится до тех пор, пока вы не перейдете по ссылке и сами не измените его. diff --git a/app/views/alchemy/passwords/edit.html.erb b/app/views/alchemy/passwords/edit.html.erb new file mode 100644 index 0000000..786a7a8 --- /dev/null +++ b/app/views/alchemy/passwords/edit.html.erb @@ -0,0 +1,27 @@ +

<%= t('.title', default: 'Change your password') %>

+ +<%= simple_form_for(@user, as: :user, url: alchemy.password_path, html: { method: :put }) do |f| %> + <%= f.error_notification %> + + <%= f.input :reset_password_token, as: :hidden %> + <%= f.full_error :reset_password_token %> + +
+ <%= f.input :password, + label: t('.password.label', default: 'New password'), + required: true, + autofocus: true, + hint: t('.hint', default: '%{minimum} characters minimum', minimum: @minimum_password_length) if @minimum_password_length, + input_html: { autocomplete: "new-password" } %> + <%= f.input :password_confirmation, + label: t('.password_confirmation.label', default: 'Confirm your new password'), + required: true, + input_html: { autocomplete: "new-password" } %> +
+ +
+ <%= f.button :submit, t('.button.label', default: 'Change my password') %> +
+<% end %> + +<%= render "alchemy/devise/shared/links" %> diff --git a/app/views/alchemy/passwords/new.html.erb b/app/views/alchemy/passwords/new.html.erb new file mode 100644 index 0000000..30090da --- /dev/null +++ b/app/views/alchemy/passwords/new.html.erb @@ -0,0 +1,18 @@ +

<%= t('.title', default: 'Forgot your password?') %>

+ +<%= simple_form_for(@user, as: :user, url: alchemy.password_path, html: { method: :post }) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :email, + required: true, + autofocus: true, + input_html: { autocomplete: "email" } %> +
+ +
+ <%= f.button :submit, t('.button.label', default: 'Send me reset password instructions') %> +
+<% end %> + +<%= render "alchemy/devise/shared/links" %> diff --git a/app/views/alchemy/sessions/new.html.erb b/app/views/alchemy/sessions/new.html.erb new file mode 100644 index 0000000..5b5954b --- /dev/null +++ b/app/views/alchemy/sessions/new.html.erb @@ -0,0 +1,32 @@ +

<%= t('.title', default: 'Log in') %>

+ +<% if flash[:alert] %> +
+ <%= flash[:alert] %> +
+<% end %> + +<% if flash[:notice] %> +
+ <%= flash[:notice] %> +
+<% end %> + +<%= simple_form_for(@user, as: :user, url: alchemy.login_path) do |f| %> +
+ <%= f.input :email, + required: false, + autofocus: true, + input_html: { autocomplete: "email" } %> + <%= f.input :password, + required: false, + input_html: { autocomplete: "current-password" } %> + <%= f.input :remember_me, as: :boolean if devise_mapping.rememberable? %> +
+ +
+ <%= f.button :submit, t('.button.label', default: 'Log in') %> +
+<% end %> + +<%= render "alchemy/devise/shared/links" %> diff --git a/config/routes.rb b/config/routes.rb index 055cce5..313d553 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,36 @@ Alchemy::Engine.routes.draw do + if Alchemy::Devise.enable_user_accounts? + devise_for :user, + class_name: 'Alchemy::User', + singular: :user, + skip: :all, + controllers: { + registrations: Alchemy::Devise.registrations_enabled? ? 'alchemy/accounts' : nil, + confirmations: Alchemy::Devise.confirmations_enabled? ? 'alchemy/confirmations' : nil, + sessions: 'alchemy/sessions', + passwords: 'alchemy/passwords' + }, + path: :account, + router_name: :alchemy + + scope :account do + devise_scope :user do + get '/login' => 'sessions#new' + post '/login' => 'sessions#create' + match '/logout' => 'sessions#destroy', via: Devise.sign_out_via + + if Alchemy::Devise.confirmations_enabled? + resource :confirmation, only: %i[new create show] + end + resource :password, only: %i[new create edit update] + end + end + + devise_scope :user do + resource :account, except: Alchemy::Devise.registrations_enabled? ? [] : %i[new create] + end + end + namespace :admin, { path: Alchemy.admin_path, constraints: Alchemy.admin_constraints diff --git a/lib/alchemy/devise.rb b/lib/alchemy/devise.rb index 65eb8a2..658e1dd 100644 --- a/lib/alchemy/devise.rb +++ b/lib/alchemy/devise.rb @@ -6,12 +6,12 @@ module Alchemy # === Default modules # # [ - #. :database_authenticatable, + # :database_authenticatable, # :trackable, # :validatable, # :timeoutable, # :recoverable - #. ] + # ] # # If you want to add additional modules into the Alchemy user class append # them to this collection in an initializer in your app. @@ -36,6 +36,25 @@ def self.devise_modules ] end + def self.devise_modules=(modules) + @devise_modules = modules + end + module Devise + def self.enable_user_accounts? + @enable_user_accounts ||= false + end + + def self.enable_user_accounts=(val) + @enable_user_accounts = val + end + + def self.registrations_enabled? + Alchemy.devise_modules.include?(:registerable) + end + + def self.confirmations_enabled? + Alchemy.devise_modules.include?(:confirmable) + end end end diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb new file mode 100644 index 0000000..f8c4457 --- /dev/null +++ b/spec/controllers/accounts_controller_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +describe Alchemy::AccountsController do + routes { Alchemy::Engine.routes } + + context 'with user accounts enabled' do + before do + allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } + Rails.application.reload_routes! + @request.env["devise.mapping"] = Devise.mappings[:user] + end + + describe '#show' do + let(:user) { create(:alchemy_member_user) } + + context 'with authorized user' do + before { authorize_user(user) } + + render_views + + it 'shows account' do + get :show + is_expected.to render_template(:show) + end + end + + context 'with unauthorized user' do + it 'redirects to login' do + get :show + is_expected.to redirect_to(login_path) + end + + it 'stores current location' do + get :show + expect(session[:user_return_to]).to eq(account_path) + end + + it 'shows warning message' do + get :show + expect(flash[:warning]).to eq I18n.t(:unauthenticated, scope: 'devise.failure') + end + end + end + end +end diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb new file mode 100644 index 0000000..c34af7b --- /dev/null +++ b/spec/controllers/confirmations_controller_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +describe Alchemy::ConfirmationsController do + routes { Alchemy::Engine.routes } + + context 'with user accounts enabled' do + before do + allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } + end + + context 'with confirmations enabled' do + let(:user) { double(email: 'mail@example.com') } + + before do + allow(Alchemy::Devise).to receive(:confirmations_enabled?) { true } + Rails.application.reload_routes! + @request.env["devise.mapping"] = Devise.mappings[:user] + expect(Alchemy::User).to receive(:send_confirmation_instructions) { user } + end + + describe '#create' do + context 'with valid params' do + before do + expect(user).to receive(:errors) { [] } + end + + it "redirects to account" do + post :create, params: {user: {email: user.email}} + expect(response).to redirect_to(login_path) + end + end + + context 'without valid params' do + before do + expect(user).to receive(:errors).twice { ['Email not found'] } + end + + it "renders form" do + post :create, params: {user: {email: 'not@found'}} + is_expected.to render_template(:new) + end + end + end + end + end +end diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb new file mode 100644 index 0000000..e8108b1 --- /dev/null +++ b/spec/controllers/passwords_controller_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +describe Alchemy::PasswordsController do + routes { Alchemy::Engine.routes } + + context 'with user accounts enabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = true + Rails.application.reload_routes! + end + + before do + @request.env["devise.mapping"] = Devise.mappings[:user] + end + + let!(:user) { create(:alchemy_user) } + + describe '#create' do + context 'with valid params' do + it "redirects to login" do + post :create, params: {user: {email: user.email}} + expect(response).to redirect_to(login_path) + end + end + + context 'without valid params' do + it "renders form" do + post :create, params: {user: {email: 'not@found'}} + is_expected.to render_template(:new) + end + end + end + end +end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 0000000..751d423 --- /dev/null +++ b/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,64 @@ +require 'rails_helper' + +describe Alchemy::SessionsController do + routes { Alchemy::Engine.routes } + + context 'with user accounts enabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = true + Rails.application.reload_routes! + end + + before do + @request.env["devise.mapping"] = Devise.mappings[:user] + end + + let(:user) { create(:alchemy_user) } + + describe '#create' do + context 'with valid user' do + let(:user_params) do + { + login: user.login, + password: 's3cr3t' + } + end + + before { user } + + context 'without redirect path in session' do + it "redirects to account" do + post :create, params: {user: user_params} + expect(response).to redirect_to(account_path) + end + end + + context 'with redirect path in session' do + it "redirects to these params" do + session[:user_return_to] = '/secret_page' + post :create, params: {user: user_params} + expect(response).to redirect_to('/secret_page') + end + end + + context 'without valid params' do + it "renders login form" do + post :create, params: {user: {login: ''}} + is_expected.to render_template(:new) + end + end + end + end + + describe "#destroy" do + before do + authorize_user(user) + end + + it "redirects to root" do + delete :destroy + is_expected.to redirect_to(root_path) + end + end + end +end diff --git a/spec/mailers/notifications_spec.rb b/spec/mailers/notifications_spec.rb index 377735b..6ebfffd 100644 --- a/spec/mailers/notifications_spec.rb +++ b/spec/mailers/notifications_spec.rb @@ -78,5 +78,43 @@ module Alchemy expect(mail.body.raw_source).to match /#{Regexp.escape(admin_edit_password_url(user, reset_password_token: token, only_path: true))}/ end end + + context 'with user accounts and confirmations enabled' do + before do + allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } + allow(Alchemy::Devise).to receive(:confirmations_enabled?) { true } + Rails.application.reload_routes! + end + + describe '#confirmation_instructions' do + let(:user) do + mock_model 'User', + alchemy_roles: %w(member), + email: 'jon@doe.com', + name: 'John Doe', + login: 'jon.doe', + fullname: 'John Doe' + end + + let(:token) { '123456789' } + + let(:mail) do + Notifications.confirmation_instructions(user, token) + end + + it "delivers a mail to user" do + expect(mail.to).to eq([user.email]) + expect(mail.subject).to eq('Account confirmation instructions') + end + + it "mail body includes users name" do + expect(mail.body.raw_source).to match /#{user.fullname}/ + end + + it "mail body includes reset instructions" do + expect(mail.body.raw_source).to match /#{Regexp.escape(confirmation_url(user, confirmation_token: token, only_path: true))}/ + end + end + end end end diff --git a/spec/routing/account_routing_spec.rb b/spec/routing/account_routing_spec.rb new file mode 100644 index 0000000..2152781 --- /dev/null +++ b/spec/routing/account_routing_spec.rb @@ -0,0 +1,132 @@ +require 'rails_helper' + +describe "Account Routing" do + context 'if user accounts are enabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = true + Rails.application.reload_routes! + end + + it "routes to show account" do + expect({ + get: "/account" + }).to route_to( + controller: "alchemy/accounts", + action: "show" + ) + end + + it "routes to edit account" do + expect({ + get: "/account/edit" + }).to route_to( + controller: "alchemy/accounts", + action: "edit" + ) + end + + it "routes to update account" do + expect({ + patch: "/account" + }).to route_to( + controller: "alchemy/accounts", + action: "update" + ) + end + + it "routes to destroy account" do + expect({ + delete: "/account" + }).to route_to( + controller: "alchemy/accounts", + action: "destroy" + ) + end + + context 'when registrations are enabled' do + before do + allow(Alchemy::Devise).to receive(:registrations_enabled?) { true } + Rails.application.reload_routes! + end + + it "routes to new account" do + expect({ + get: "/account/new" + }).to route_to( + controller: "alchemy/accounts", + action: "new" + ) + end + + it "routes to create account" do + expect({ + post: "/account" + }).to route_to( + controller: "alchemy/accounts", + action: "create" + ) + end + end + end + + context 'if user accounts are disabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = false + Rails.application.reload_routes! + end + + it "does not route to show account" do + expect({ + get: "/account" + }).to_not route_to( + controller: "alchemy/accounts", + action: "show" + ) + end + + it "does not route to edit account" do + expect({ + get: "/account/edit" + }).to_not route_to( + controller: "alchemy/accounts", + action: "edit" + ) + end + + it "does not route to update account" do + expect({ + patch: "/account" + }).to_not route_to( + controller: "alchemy/accounts", + action: "update" + ) + end + + it "does not route to destroy account" do + expect({ + delete: "/account" + }).to_not route_to( + controller: "alchemy/accounts", + action: "destroy" + ) + end + + it "does not route to new account" do + expect({ + get: "/account/new" + }).to_not route_to( + controller: "alchemy/accounts", + action: "new" + ) + end + + it "does not route to create account" do + expect({ + post: "/account" + }).to_not route_to( + controller: "alchemy/accounts", + action: "create" + ) + end + end +end diff --git a/spec/routing/admin_password_routing_spec.rb b/spec/routing/admin_password_routing_spec.rb new file mode 100644 index 0000000..4ac1c23 --- /dev/null +++ b/spec/routing/admin_password_routing_spec.rb @@ -0,0 +1,43 @@ +require 'rails_helper' + +describe "Admin Password Routing" do + routes { Alchemy::Engine.routes } + + it "routes to new password" do + expect({ + get: "/admin/passwords" + }).to route_to( + controller: "alchemy/admin/passwords", + action: "new" + ) + end + + it "routes to reset password" do + expect({ + post: "/admin/passwords" + }).to route_to( + controller: "alchemy/admin/passwords", + action: "create" + ) + end + + it "routes to edit password" do + expect({ + get: "/admin/passwords/123/edit/12345" + }).to route_to( + controller: "alchemy/admin/passwords", + action: "edit", + id: "123", + reset_password_token: "12345" + ) + end + + it "routes to update password" do + expect({ + patch: "/admin/passwords" + }).to route_to( + controller: "alchemy/admin/passwords", + action: "update" + ) + end +end diff --git a/spec/routing/admin_user_session_routing_spec.rb b/spec/routing/admin_user_session_routing_spec.rb new file mode 100644 index 0000000..826fb8c --- /dev/null +++ b/spec/routing/admin_user_session_routing_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' + +describe "Admin User Session Routing" do + routes { Alchemy::Engine.routes } + + it "routes to login" do + expect({ + get: "/admin/login" + }).to route_to( + controller: "alchemy/admin/user_sessions", + action: "new" + ) + end + + it "routes to create session" do + expect({ + post: "/admin/login" + }).to route_to( + controller: "alchemy/admin/user_sessions", + action: "create" + ) + end + + it "routes to logout" do + expect({ + delete: "/admin/logout" + }).to route_to( + controller: "alchemy/admin/user_sessions", + action: "destroy" + ) + end +end diff --git a/spec/routing/password_routing_spec.rb b/spec/routing/password_routing_spec.rb index 008e577..851c576 100644 --- a/spec/routing/password_routing_spec.rb +++ b/spec/routing/password_routing_spec.rb @@ -1,43 +1,89 @@ require 'rails_helper' describe "Password Routing" do - routes { Alchemy::Engine.routes } - - it "routes to new password" do - expect({ - get: "/admin/passwords" - }).to route_to( - controller: "alchemy/admin/passwords", - action: "new" - ) - end + context 'if user accounts are enabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = true + Rails.application.reload_routes! + end - it "routes to reset password" do - expect({ - post: "/admin/passwords" - }).to route_to( - controller: "alchemy/admin/passwords", - action: "create" - ) - end + it "routes to new password" do + expect({ + get: "/account/password/new" + }).to route_to( + controller: "alchemy/passwords", + action: "new" + ) + end + + it "routes to reset password" do + expect({ + post: "/account/password" + }).to route_to( + controller: "alchemy/passwords", + action: "create" + ) + end + + it "routes to edit password" do + expect({ + get: "/account/password/edit" + }).to route_to( + controller: "alchemy/passwords", + action: "edit" + ) + end - it "routes to edit password" do - expect({ - get: "/admin/passwords/123/edit/12345" - }).to route_to( - controller: "alchemy/admin/passwords", - action: "edit", - id: "123", - reset_password_token: "12345" - ) + it "routes to update password" do + expect({ + patch: "/account/password" + }).to route_to( + controller: "alchemy/passwords", + action: "update" + ) + end end - it "routes to update password" do - expect({ - patch: "/admin/passwords" - }).to route_to( - controller: "alchemy/admin/passwords", - action: "update" - ) + context 'if user accounts are disabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = false + Rails.application.reload_routes! + end + + it "does not route to new password" do + expect({ + get: "/account/password/new" + }).to_not route_to( + controller: "alchemy/passwords", + action: "new" + ) + end + + it "does not route to reset password" do + expect({ + post: "/account/password" + }).to_not route_to( + controller: "alchemy/passwords", + action: "create" + ) + end + + it "does not route to edit password" do + expect({ + get: "/account/password/edit" + }).to_not route_to( + controller: "alchemy/passwords", + action: "edit" + ) + end + + it "does not route to update password" do + expect({ + patch: "/account/password" + }).to_not route_to( + controller: "alchemy/passwords", + action: "update" + ) + end end end diff --git a/spec/routing/session_routing_spec.rb b/spec/routing/session_routing_spec.rb index 0d6b9a9..58b0801 100644 --- a/spec/routing/session_routing_spec.rb +++ b/spec/routing/session_routing_spec.rb @@ -1,32 +1,71 @@ require 'rails_helper' describe "Session Routing" do - routes { Alchemy::Engine.routes } - - it "routes to login" do - expect({ - get: "/admin/login" - }).to route_to( - controller: "alchemy/admin/user_sessions", - action: "new" - ) - end + context 'if user accounts are enabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = true + Rails.application.reload_routes! + end + + it "routes to login" do + expect({ + get: "/account/login" + }).to route_to( + controller: "alchemy/sessions", + action: "new" + ) + end + + it "routes to create session" do + expect({ + post: "/account/login" + }).to route_to( + controller: "alchemy/sessions", + action: "create" + ) + end - it "routes to create session" do - expect({ - post: "/admin/login" - }).to route_to( - controller: "alchemy/admin/user_sessions", - action: "create" - ) + it "routes to logout" do + expect({ + delete: "/account/logout" + }).to route_to( + controller: "alchemy/sessions", + action: "destroy" + ) + end end - it "routes to logout" do - expect({ - delete: "/admin/logout" - }).to route_to( - controller: "alchemy/admin/user_sessions", - action: "destroy" - ) + context 'if user accounts are disabled' do + before(:all) do + Alchemy::Devise.enable_user_accounts = false + Rails.application.reload_routes! + end + + it "does not route to login" do + expect({ + get: "/account/login" + }).to_not route_to( + controller: "alchemy/sessions", + action: "new" + ) + end + + it "does not route to create session" do + expect({ + post: "/account/login" + }).to_not route_to( + controller: "alchemy/sessions", + action: "create" + ) + end + + it "does not route to logout" do + expect({ + delete: "/account/logout" + }).to_not route_to( + controller: "alchemy/sessions", + action: "destroy" + ) + end end end From 84514273a1e558cdd5ed95973ab6edecb09953c7 Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Tue, 17 Mar 2020 23:49:53 +0100 Subject: [PATCH 2/5] Move admin feature specs into correct folder --- spec/features/{ => admin}/login_feature_spec.rb | 2 +- spec/features/{ => admin}/password_reset_feature_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename spec/features/{ => admin}/login_feature_spec.rb (98%) rename spec/features/{ => admin}/password_reset_feature_spec.rb (95%) diff --git a/spec/features/login_feature_spec.rb b/spec/features/admin/login_feature_spec.rb similarity index 98% rename from spec/features/login_feature_spec.rb rename to spec/features/admin/login_feature_spec.rb index da7a6fe..1742e56 100644 --- a/spec/features/login_feature_spec.rb +++ b/spec/features/admin/login_feature_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe "Login: " do +describe "Admin Login: " do context "If user is present" do let!(:user) do Alchemy::User.create!( diff --git a/spec/features/password_reset_feature_spec.rb b/spec/features/admin/password_reset_feature_spec.rb similarity index 95% rename from spec/features/password_reset_feature_spec.rb rename to spec/features/admin/password_reset_feature_spec.rb index 30d1d3d..89574bf 100644 --- a/spec/features/password_reset_feature_spec.rb +++ b/spec/features/admin/password_reset_feature_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe "Password reset feature." do +describe "Admin password reset feature." do let(:user) { create(:alchemy_admin_user) } it "User can visit password reset form." do From e5cd0192a91ae165ea2f17ebbd111d59facb741c Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Tue, 17 Mar 2020 23:53:50 +0100 Subject: [PATCH 3/5] Return to admin dashboard after resetting admin password --- app/controllers/alchemy/admin/passwords_controller.rb | 6 +----- spec/features/admin/password_reset_feature_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/alchemy/admin/passwords_controller.rb b/app/controllers/alchemy/admin/passwords_controller.rb index df569e8..7889e0f 100644 --- a/app/controllers/alchemy/admin/passwords_controller.rb +++ b/app/controllers/alchemy/admin/passwords_controller.rb @@ -27,11 +27,7 @@ def admin_edit_password_url(_resource, options = {}) end def after_resetting_password_path_for(resource) - if can? :index, :alchemy_admin_dashboard - alchemy.admin_dashboard_path - else - alchemy.root_path - end + alchemy.admin_dashboard_path end end end diff --git a/spec/features/admin/password_reset_feature_spec.rb b/spec/features/admin/password_reset_feature_spec.rb index 89574bf..f332bf1 100644 --- a/spec/features/admin/password_reset_feature_spec.rb +++ b/spec/features/admin/password_reset_feature_spec.rb @@ -30,6 +30,8 @@ fill_in :user_password_confirmation, with: 'secret123' click_button 'Change password' + expect(page.current_path).to eq(alchemy.admin_dashboard_path) + expect(page) .to have_content('Your password has been changed successfully.') end From 84c40ee7feb0584fe604eea354eb667b5ec191b0 Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Wed, 18 Mar 2020 00:01:06 +0100 Subject: [PATCH 4/5] Remove unused method from admin passwords controller Neither we nor Devise is calling this method --- app/controllers/alchemy/admin/passwords_controller.rb | 4 ---- spec/features/admin/password_reset_feature_spec.rb | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/controllers/alchemy/admin/passwords_controller.rb b/app/controllers/alchemy/admin/passwords_controller.rb index 7889e0f..77d351a 100644 --- a/app/controllers/alchemy/admin/passwords_controller.rb +++ b/app/controllers/alchemy/admin/passwords_controller.rb @@ -22,10 +22,6 @@ def new_session_path(resource_name) alchemy.admin_login_path end - def admin_edit_password_url(_resource, options = {}) - alchemy.admin_edit_password_url(options) - end - def after_resetting_password_path_for(resource) alchemy.admin_dashboard_path end diff --git a/spec/features/admin/password_reset_feature_spec.rb b/spec/features/admin/password_reset_feature_spec.rb index f332bf1..a537fcc 100644 --- a/spec/features/admin/password_reset_feature_spec.rb +++ b/spec/features/admin/password_reset_feature_spec.rb @@ -20,7 +20,7 @@ end it "User can change password." do - allow(Alchemy::User) + expect(Alchemy::User) .to receive(:reset_password_by_token) .and_return(user) From a05ed82db551e1ac5a95d56c24ef7c111c827af4 Mon Sep 17 00:00:00 2001 From: Thomas von Deyen Date: Mon, 26 Oct 2020 22:03:48 +0100 Subject: [PATCH 5/5] Autoformat with Rufo --- .../alchemy/accounts_controller.rb | 6 +- .../alchemy/confirmations_controller.rb | 4 +- .../alchemy/passwords_controller.rb | 4 +- .../alchemy/sessions_controller.rb | 4 +- app/mailers/alchemy/notifications.rb | 21 +++---- app/models/alchemy/user.rb | 34 ++++++----- config/routes.rb | 45 +++++++------- spec/controllers/accounts_controller_spec.rb | 22 +++---- .../confirmations_controller_spec.rb | 22 +++---- spec/controllers/passwords_controller_spec.rb | 16 ++--- spec/controllers/sessions_controller_spec.rb | 28 ++++----- spec/mailers/notifications_spec.rb | 53 ++++++++--------- spec/routing/account_routing_spec.rb | 58 ++++++++++--------- spec/routing/admin_password_routing_spec.rb | 20 ++++--- .../admin_user_session_routing_spec.rb | 16 ++--- spec/routing/password_routing_spec.rb | 40 +++++++------ spec/routing/session_routing_spec.rb | 32 +++++----- 17 files changed, 228 insertions(+), 197 deletions(-) diff --git a/app/controllers/alchemy/accounts_controller.rb b/app/controllers/alchemy/accounts_controller.rb index 13bf743..ba8a843 100644 --- a/app/controllers/alchemy/accounts_controller.rb +++ b/app/controllers/alchemy/accounts_controller.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Alchemy class AccountsController < ::Devise::RegistrationsController - helper 'Alchemy::Pages' + helper "Alchemy::Pages" def show authorize! :show, current_alchemy_user @@ -11,7 +13,7 @@ def show def permission_denied(*) store_location_for(:user, account_path) - flash[:warning] = t(:unauthenticated, scope: 'devise.failure') + flash[:warning] = t(:unauthenticated, scope: "devise.failure") redirect_to alchemy.login_path end end diff --git a/app/controllers/alchemy/confirmations_controller.rb b/app/controllers/alchemy/confirmations_controller.rb index daefa26..354b1ed 100644 --- a/app/controllers/alchemy/confirmations_controller.rb +++ b/app/controllers/alchemy/confirmations_controller.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Alchemy class ConfirmationsController < ::Devise::ConfirmationsController - helper 'Alchemy::Pages' + helper "Alchemy::Pages" private diff --git a/app/controllers/alchemy/passwords_controller.rb b/app/controllers/alchemy/passwords_controller.rb index 52f87bb..c90176f 100644 --- a/app/controllers/alchemy/passwords_controller.rb +++ b/app/controllers/alchemy/passwords_controller.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Alchemy class PasswordsController < ::Devise::PasswordsController - helper 'Alchemy::Pages' + helper "Alchemy::Pages" private diff --git a/app/controllers/alchemy/sessions_controller.rb b/app/controllers/alchemy/sessions_controller.rb index d119f16..89954af 100644 --- a/app/controllers/alchemy/sessions_controller.rb +++ b/app/controllers/alchemy/sessions_controller.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module Alchemy class SessionsController < ::Devise::SessionsController - helper 'Alchemy::Pages' + helper "Alchemy::Pages" private diff --git a/app/mailers/alchemy/notifications.rb b/app/mailers/alchemy/notifications.rb index cd27825..d2fede3 100644 --- a/app/mailers/alchemy/notifications.rb +++ b/app/mailers/alchemy/notifications.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + module Alchemy class Notifications < ::Devise::Mailer - - default(from: Config.get(:mailer)['mail_from']) + default(from: Config.get(:mailer)["mail_from"]) def member_created(user) @user = user mail( to: user.email, - subject: Alchemy.t("Your user credentials") + subject: Alchemy.t("Your user credentials"), ) end @@ -17,34 +18,34 @@ def alchemy_user_created(user) @url = admin_url mail( to: user.email, - subject: Alchemy.t("Your Alchemy Login") + subject: Alchemy.t("Your Alchemy Login"), ) end - def member_reset_password_instructions(user, token, opts={}) + def member_reset_password_instructions(user, token, _opts = {}) @user = user @token = token mail( to: user.email, - subject: Alchemy.t("Reset password instructions") + subject: Alchemy.t("Reset password instructions"), ) end - def reset_password_instructions(user, token, opts={}) + def reset_password_instructions(user, token, _opts = {}) @user = user @token = token mail( to: user.email, - subject: Alchemy.t("Reset password instructions") + subject: Alchemy.t("Reset password instructions"), ) end - def confirmation_instructions(user, token, opts={}) + def confirmation_instructions(user, token, _opts = {}) @user = user @token = token mail( to: user.email, - subject: Alchemy.t("Account confirmation instructions") + subject: Alchemy.t("Account confirmation instructions"), ) end end diff --git a/app/models/alchemy/user.rb b/app/models/alchemy/user.rb index 7c042fe..d423f5d 100644 --- a/app/models/alchemy/user.rb +++ b/app/models/alchemy/user.rb @@ -1,5 +1,7 @@ -require 'devise/orm/active_record' -require 'userstamp' +# frozen_string_literal: true + +require "devise/orm/active_record" +require "userstamp" module Alchemy class User < ActiveRecord::Base @@ -12,7 +14,7 @@ class User < ActiveRecord::Base :password, :password_confirmation, :send_credentials, - :tag_list + :tag_list, ] devise *Alchemy.devise_modules @@ -29,9 +31,9 @@ class User < ActiveRecord::Base # Unlock all locked pages before destroy. before_destroy :unlock_pages! - scope :admins, -> { where(arel_table[:alchemy_roles].matches('%admin%')) } - scope :logged_in, -> { where('last_request_at > ?', logged_in_timeout.seconds.ago) } - scope :logged_out, -> { where('last_request_at is NULL or last_request_at <= ?', logged_in_timeout.seconds.ago) } + scope :admins, -> { where(arel_table[:alchemy_roles].matches("%admin%")) } + scope :logged_in, -> { where("last_request_at > ?", logged_in_timeout.seconds.ago) } + scope :logged_out, -> { where("last_request_at is NULL or last_request_at <= ?", logged_in_timeout.seconds.ago) } ROLES = Config.get(:user_roles) @@ -51,10 +53,7 @@ def logged_in_timeout def search(query) query = "%#{query.downcase}%" - where arel_table[:login].lower.matches(query) - .or arel_table[:email].lower.matches(query) - .or arel_table[:firstname].lower.matches(query) - .or arel_table[:lastname].lower.matches(query) + where arel_table[:login].lower.matches(query).or arel_table[:email].lower.matches(query).or arel_table[:firstname].lower.matches(query).or arel_table[:lastname].lower.matches(query) end end @@ -67,12 +66,12 @@ def role end def alchemy_roles - read_attribute(:alchemy_roles).split(' ') + read_attribute(:alchemy_roles).split(" ") end def alchemy_roles=(roles_string) if roles_string.is_a? Array - write_attribute(:alchemy_roles, roles_string.join(' ')) + write_attribute(:alchemy_roles, roles_string.join(" ")) elsif roles_string.is_a? String write_attribute(:alchemy_roles, roles_string) end @@ -84,8 +83,9 @@ def add_role(role) # Returns true if the user ahs admin role def is_admin? - has_role? 'admin' + has_role? "admin" end + alias_method :admin?, :is_admin? # Returns true if the user has the given role. @@ -105,6 +105,7 @@ def unlock_pages! def pages_locked_by_me Page.locked_by(self).order(:updated_at) end + alias_method :locked_pages, :pages_locked_by_me # Returns the firstname and lastname as a string @@ -118,11 +119,12 @@ def fullname(options = {}) if lastname.blank? && firstname.blank? login else - options = {:flipped => false}.merge(options) + options = { flipped: false }.merge(options) fullname = options[:flipped] ? "#{lastname}, #{firstname}" : "#{firstname} #{lastname}" fullname.squeeze(" ").strip end end + alias_method :name, :fullname alias_method :alchemy_display_name, :fullname @@ -150,7 +152,7 @@ def store_request_time! # Delivers a welcome mail depending from user's role. # def deliver_welcome_mail - if has_role?('author') || has_role?('editor') || has_role?('admin') + if has_role?("author") || has_role?("editor") || has_role?("admin") Notifications.alchemy_user_created(self).deliver_later else Notifications.member_created(self).deliver_later @@ -159,7 +161,7 @@ def deliver_welcome_mail # Overwritten to send a different email to members def send_reset_password_instructions_notification(token) - if has_role?('member') + if has_role?("member") send_devise_notification(:member_reset_password_instructions, token, {}) else send_devise_notification(:reset_password_instructions, token, {}) diff --git a/config/routes.rb b/config/routes.rb index 313d553..c3cfbe7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,23 +1,25 @@ +# frozen_string_literal: true + Alchemy::Engine.routes.draw do if Alchemy::Devise.enable_user_accounts? devise_for :user, - class_name: 'Alchemy::User', + class_name: "Alchemy::User", singular: :user, skip: :all, controllers: { - registrations: Alchemy::Devise.registrations_enabled? ? 'alchemy/accounts' : nil, - confirmations: Alchemy::Devise.confirmations_enabled? ? 'alchemy/confirmations' : nil, - sessions: 'alchemy/sessions', - passwords: 'alchemy/passwords' + registrations: Alchemy::Devise.registrations_enabled? ? "alchemy/accounts" : nil, + confirmations: Alchemy::Devise.confirmations_enabled? ? "alchemy/confirmations" : nil, + sessions: "alchemy/sessions", + passwords: "alchemy/passwords", }, path: :account, router_name: :alchemy scope :account do devise_scope :user do - get '/login' => 'sessions#new' - post '/login' => 'sessions#create' - match '/logout' => 'sessions#destroy', via: Devise.sign_out_via + get "/login" => "sessions#new" + post "/login" => "sessions#create" + match "/logout" => "sessions#destroy", via: Devise.sign_out_via if Alchemy::Devise.confirmations_enabled? resource :confirmation, only: %i[new create show] @@ -33,37 +35,36 @@ namespace :admin, { path: Alchemy.admin_path, - constraints: Alchemy.admin_constraints + constraints: Alchemy.admin_constraints, } do - devise_for :user, - class_name: 'Alchemy::User', + class_name: "Alchemy::User", singular: :user, skip: :all, controllers: { - sessions: 'alchemy/admin/user_sessions', - passwords: 'alchemy/admin/passwords' + sessions: "alchemy/admin/user_sessions", + passwords: "alchemy/admin/passwords", }, router_name: :alchemy devise_scope :user do - get '/dashboard' => 'dashboard#index', + get "/dashboard" => "dashboard#index", :as => :user_root - get '/signup' => 'users#signup', + get "/signup" => "users#signup", :as => :signup - get '/login' => 'user_sessions#new', + get "/login" => "user_sessions#new", :as => :login - post '/login' => 'user_sessions#create' - match '/logout' => 'user_sessions#destroy', + post "/login" => "user_sessions#create" + match "/logout" => "user_sessions#destroy", :as => :logout, via: Devise.sign_out_via - get '/passwords' => 'passwords#new', + get "/passwords" => "passwords#new", :as => :new_password - get '/passwords/:id/edit/:reset_password_token' => 'passwords#edit', + get "/passwords/:id/edit/:reset_password_token" => "passwords#edit", :as => :edit_password - post '/passwords' => 'passwords#create', + post "/passwords" => "passwords#create", :as => :reset_password - patch '/passwords' => 'passwords#update', + patch "/passwords" => "passwords#update", :as => :update_password end diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index f8c4457..0122c45 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -1,43 +1,45 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe Alchemy::AccountsController do routes { Alchemy::Engine.routes } - context 'with user accounts enabled' do + context "with user accounts enabled" do before do allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } Rails.application.reload_routes! @request.env["devise.mapping"] = Devise.mappings[:user] end - describe '#show' do + describe "#show" do let(:user) { create(:alchemy_member_user) } - context 'with authorized user' do + context "with authorized user" do before { authorize_user(user) } render_views - it 'shows account' do + it "shows account" do get :show is_expected.to render_template(:show) end end - context 'with unauthorized user' do - it 'redirects to login' do + context "with unauthorized user" do + it "redirects to login" do get :show is_expected.to redirect_to(login_path) end - it 'stores current location' do + it "stores current location" do get :show expect(session[:user_return_to]).to eq(account_path) end - it 'shows warning message' do + it "shows warning message" do get :show - expect(flash[:warning]).to eq I18n.t(:unauthenticated, scope: 'devise.failure') + expect(flash[:warning]).to eq I18n.t(:unauthenticated, scope: "devise.failure") end end end diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb index c34af7b..21f9879 100644 --- a/spec/controllers/confirmations_controller_spec.rb +++ b/spec/controllers/confirmations_controller_spec.rb @@ -1,15 +1,17 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe Alchemy::ConfirmationsController do routes { Alchemy::Engine.routes } - context 'with user accounts enabled' do + context "with user accounts enabled" do before do allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } end - context 'with confirmations enabled' do - let(:user) { double(email: 'mail@example.com') } + context "with confirmations enabled" do + let(:user) { double(email: "mail@example.com") } before do allow(Alchemy::Devise).to receive(:confirmations_enabled?) { true } @@ -18,25 +20,25 @@ expect(Alchemy::User).to receive(:send_confirmation_instructions) { user } end - describe '#create' do - context 'with valid params' do + describe "#create" do + context "with valid params" do before do expect(user).to receive(:errors) { [] } end it "redirects to account" do - post :create, params: {user: {email: user.email}} + post :create, params: { user: { email: user.email } } expect(response).to redirect_to(login_path) end end - context 'without valid params' do + context "without valid params" do before do - expect(user).to receive(:errors).twice { ['Email not found'] } + expect(user).to receive(:errors).twice { ["Email not found"] } end it "renders form" do - post :create, params: {user: {email: 'not@found'}} + post :create, params: { user: { email: "not@found" } } is_expected.to render_template(:new) end end diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb index e8108b1..79d3a22 100644 --- a/spec/controllers/passwords_controller_spec.rb +++ b/spec/controllers/passwords_controller_spec.rb @@ -1,9 +1,11 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe Alchemy::PasswordsController do routes { Alchemy::Engine.routes } - context 'with user accounts enabled' do + context "with user accounts enabled" do before(:all) do Alchemy::Devise.enable_user_accounts = true Rails.application.reload_routes! @@ -15,17 +17,17 @@ let!(:user) { create(:alchemy_user) } - describe '#create' do - context 'with valid params' do + describe "#create" do + context "with valid params" do it "redirects to login" do - post :create, params: {user: {email: user.email}} + post :create, params: { user: { email: user.email } } expect(response).to redirect_to(login_path) end end - context 'without valid params' do + context "without valid params" do it "renders form" do - post :create, params: {user: {email: 'not@found'}} + post :create, params: { user: { email: "not@found" } } is_expected.to render_template(:new) end end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 751d423..497da99 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -1,9 +1,11 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe Alchemy::SessionsController do routes { Alchemy::Engine.routes } - context 'with user accounts enabled' do + context "with user accounts enabled" do before(:all) do Alchemy::Devise.enable_user_accounts = true Rails.application.reload_routes! @@ -15,35 +17,35 @@ let(:user) { create(:alchemy_user) } - describe '#create' do - context 'with valid user' do + describe "#create" do + context "with valid user" do let(:user_params) do { login: user.login, - password: 's3cr3t' + password: "s3cr3t", } end before { user } - context 'without redirect path in session' do + context "without redirect path in session" do it "redirects to account" do - post :create, params: {user: user_params} + post :create, params: { user: user_params } expect(response).to redirect_to(account_path) end end - context 'with redirect path in session' do + context "with redirect path in session" do it "redirects to these params" do - session[:user_return_to] = '/secret_page' - post :create, params: {user: user_params} - expect(response).to redirect_to('/secret_page') + session[:user_return_to] = "/secret_page" + post :create, params: { user: user_params } + expect(response).to redirect_to("/secret_page") end end - context 'without valid params' do + context "without valid params" do it "renders login form" do - post :create, params: {user: {login: ''}} + post :create, params: { user: { login: "" } } is_expected.to render_template(:new) end end diff --git a/spec/mailers/notifications_spec.rb b/spec/mailers/notifications_spec.rb index 6ebfffd..705467b 100644 --- a/spec/mailers/notifications_spec.rb +++ b/spec/mailers/notifications_spec.rb @@ -1,21 +1,22 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" module Alchemy describe Notifications do - context "when a member user was created" do let(:user) do - mock_model 'User', + mock_model "User", alchemy_roles: %w(member), - email: 'jon@doe.com', - name: 'John Doe', - login: 'jon.doe' + email: "jon@doe.com", + name: "John Doe", + login: "jon.doe" end let(:mail) { Notifications.member_created(user) } it "delivers a mail to user" do expect(mail.to).to eq([user.email]) - expect(mail.subject).to eq('Your user credentials') + expect(mail.subject).to eq("Your user credentials") end it "mail body includes users name" do @@ -32,12 +33,12 @@ module Alchemy end context "when an admin user was created" do - let(:user) { mock_model('User', alchemy_roles: %w(admin), email: 'jon@doe.com', name: 'John Doe', login: 'jon.doe') } + let(:user) { mock_model("User", alchemy_roles: %w(admin), email: "jon@doe.com", name: "John Doe", login: "jon.doe") } let(:mail) { Notifications.alchemy_user_created(user) } it "delivers a mail to user" do expect(mail.to).to eq([user.email]) - expect(mail.subject).to eq('Your Alchemy Login') + expect(mail.subject).to eq("Your Alchemy Login") end it "mail body includes users login" do @@ -49,17 +50,17 @@ module Alchemy end end - describe '#reset_password_instructions' do + describe "#reset_password_instructions" do let(:user) do - mock_model 'User', + mock_model "User", alchemy_roles: %w(member), - email: 'jon@doe.com', - name: 'John Doe', - login: 'jon.doe', - fullname: 'John Doe' + email: "jon@doe.com", + name: "John Doe", + login: "jon.doe", + fullname: "John Doe" end - let(:token) { '123456789' } + let(:token) { "123456789" } let(:mail) do Notifications.reset_password_instructions(user, token) @@ -67,7 +68,7 @@ module Alchemy it "delivers a mail to user" do expect(mail.to).to eq([user.email]) - expect(mail.subject).to eq('Reset password instructions') + expect(mail.subject).to eq("Reset password instructions") end it "mail body includes users name" do @@ -79,24 +80,24 @@ module Alchemy end end - context 'with user accounts and confirmations enabled' do + context "with user accounts and confirmations enabled" do before do allow(Alchemy::Devise).to receive(:enable_user_accounts?) { true } allow(Alchemy::Devise).to receive(:confirmations_enabled?) { true } Rails.application.reload_routes! end - describe '#confirmation_instructions' do + describe "#confirmation_instructions" do let(:user) do - mock_model 'User', + mock_model "User", alchemy_roles: %w(member), - email: 'jon@doe.com', - name: 'John Doe', - login: 'jon.doe', - fullname: 'John Doe' + email: "jon@doe.com", + name: "John Doe", + login: "jon.doe", + fullname: "John Doe" end - let(:token) { '123456789' } + let(:token) { "123456789" } let(:mail) do Notifications.confirmation_instructions(user, token) @@ -104,7 +105,7 @@ module Alchemy it "delivers a mail to user" do expect(mail.to).to eq([user.email]) - expect(mail.subject).to eq('Account confirmation instructions') + expect(mail.subject).to eq("Account confirmation instructions") end it "mail body includes users name" do diff --git a/spec/routing/account_routing_spec.rb b/spec/routing/account_routing_spec.rb index 2152781..a221298 100644 --- a/spec/routing/account_routing_spec.rb +++ b/spec/routing/account_routing_spec.rb @@ -1,7 +1,9 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe "Account Routing" do - context 'if user accounts are enabled' do + context "if user accounts are enabled" do before(:all) do Alchemy::Devise.enable_user_accounts = true Rails.application.reload_routes! @@ -9,41 +11,41 @@ it "routes to show account" do expect({ - get: "/account" + get: "/account", }).to route_to( controller: "alchemy/accounts", - action: "show" + action: "show", ) end it "routes to edit account" do expect({ - get: "/account/edit" + get: "/account/edit", }).to route_to( controller: "alchemy/accounts", - action: "edit" + action: "edit", ) end it "routes to update account" do expect({ - patch: "/account" + patch: "/account", }).to route_to( controller: "alchemy/accounts", - action: "update" + action: "update", ) end it "routes to destroy account" do expect({ - delete: "/account" + delete: "/account", }).to route_to( controller: "alchemy/accounts", - action: "destroy" + action: "destroy", ) end - context 'when registrations are enabled' do + context "when registrations are enabled" do before do allow(Alchemy::Devise).to receive(:registrations_enabled?) { true } Rails.application.reload_routes! @@ -51,25 +53,25 @@ it "routes to new account" do expect({ - get: "/account/new" + get: "/account/new", }).to route_to( controller: "alchemy/accounts", - action: "new" + action: "new", ) end it "routes to create account" do expect({ - post: "/account" + post: "/account", }).to route_to( controller: "alchemy/accounts", - action: "create" + action: "create", ) end end end - context 'if user accounts are disabled' do + context "if user accounts are disabled" do before(:all) do Alchemy::Devise.enable_user_accounts = false Rails.application.reload_routes! @@ -77,55 +79,55 @@ it "does not route to show account" do expect({ - get: "/account" + get: "/account", }).to_not route_to( controller: "alchemy/accounts", - action: "show" + action: "show", ) end it "does not route to edit account" do expect({ - get: "/account/edit" + get: "/account/edit", }).to_not route_to( controller: "alchemy/accounts", - action: "edit" + action: "edit", ) end it "does not route to update account" do expect({ - patch: "/account" + patch: "/account", }).to_not route_to( controller: "alchemy/accounts", - action: "update" + action: "update", ) end it "does not route to destroy account" do expect({ - delete: "/account" + delete: "/account", }).to_not route_to( controller: "alchemy/accounts", - action: "destroy" + action: "destroy", ) end it "does not route to new account" do expect({ - get: "/account/new" + get: "/account/new", }).to_not route_to( controller: "alchemy/accounts", - action: "new" + action: "new", ) end it "does not route to create account" do expect({ - post: "/account" + post: "/account", }).to_not route_to( controller: "alchemy/accounts", - action: "create" + action: "create", ) end end diff --git a/spec/routing/admin_password_routing_spec.rb b/spec/routing/admin_password_routing_spec.rb index 4ac1c23..7d0f372 100644 --- a/spec/routing/admin_password_routing_spec.rb +++ b/spec/routing/admin_password_routing_spec.rb @@ -1,43 +1,45 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe "Admin Password Routing" do routes { Alchemy::Engine.routes } it "routes to new password" do expect({ - get: "/admin/passwords" + get: "/admin/passwords", }).to route_to( controller: "alchemy/admin/passwords", - action: "new" + action: "new", ) end it "routes to reset password" do expect({ - post: "/admin/passwords" + post: "/admin/passwords", }).to route_to( controller: "alchemy/admin/passwords", - action: "create" + action: "create", ) end it "routes to edit password" do expect({ - get: "/admin/passwords/123/edit/12345" + get: "/admin/passwords/123/edit/12345", }).to route_to( controller: "alchemy/admin/passwords", action: "edit", id: "123", - reset_password_token: "12345" + reset_password_token: "12345", ) end it "routes to update password" do expect({ - patch: "/admin/passwords" + patch: "/admin/passwords", }).to route_to( controller: "alchemy/admin/passwords", - action: "update" + action: "update", ) end end diff --git a/spec/routing/admin_user_session_routing_spec.rb b/spec/routing/admin_user_session_routing_spec.rb index 826fb8c..49d267d 100644 --- a/spec/routing/admin_user_session_routing_spec.rb +++ b/spec/routing/admin_user_session_routing_spec.rb @@ -1,32 +1,34 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe "Admin User Session Routing" do routes { Alchemy::Engine.routes } it "routes to login" do expect({ - get: "/admin/login" + get: "/admin/login", }).to route_to( controller: "alchemy/admin/user_sessions", - action: "new" + action: "new", ) end it "routes to create session" do expect({ - post: "/admin/login" + post: "/admin/login", }).to route_to( controller: "alchemy/admin/user_sessions", - action: "create" + action: "create", ) end it "routes to logout" do expect({ - delete: "/admin/logout" + delete: "/admin/logout", }).to route_to( controller: "alchemy/admin/user_sessions", - action: "destroy" + action: "destroy", ) end end diff --git a/spec/routing/password_routing_spec.rb b/spec/routing/password_routing_spec.rb index 851c576..d5e3839 100644 --- a/spec/routing/password_routing_spec.rb +++ b/spec/routing/password_routing_spec.rb @@ -1,7 +1,9 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe "Password Routing" do - context 'if user accounts are enabled' do + context "if user accounts are enabled" do before(:all) do Alchemy::Devise.enable_user_accounts = true Rails.application.reload_routes! @@ -9,42 +11,42 @@ it "routes to new password" do expect({ - get: "/account/password/new" + get: "/account/password/new", }).to route_to( controller: "alchemy/passwords", - action: "new" + action: "new", ) end it "routes to reset password" do expect({ - post: "/account/password" + post: "/account/password", }).to route_to( controller: "alchemy/passwords", - action: "create" + action: "create", ) end it "routes to edit password" do expect({ - get: "/account/password/edit" + get: "/account/password/edit", }).to route_to( controller: "alchemy/passwords", - action: "edit" + action: "edit", ) end it "routes to update password" do expect({ - patch: "/account/password" + patch: "/account/password", }).to route_to( controller: "alchemy/passwords", - action: "update" + action: "update", ) end end - context 'if user accounts are disabled' do + context "if user accounts are disabled" do before(:all) do Alchemy::Devise.enable_user_accounts = false Rails.application.reload_routes! @@ -52,37 +54,37 @@ it "does not route to new password" do expect({ - get: "/account/password/new" + get: "/account/password/new", }).to_not route_to( controller: "alchemy/passwords", - action: "new" + action: "new", ) end it "does not route to reset password" do expect({ - post: "/account/password" + post: "/account/password", }).to_not route_to( controller: "alchemy/passwords", - action: "create" + action: "create", ) end it "does not route to edit password" do expect({ - get: "/account/password/edit" + get: "/account/password/edit", }).to_not route_to( controller: "alchemy/passwords", - action: "edit" + action: "edit", ) end it "does not route to update password" do expect({ - patch: "/account/password" + patch: "/account/password", }).to_not route_to( controller: "alchemy/passwords", - action: "update" + action: "update", ) end end diff --git a/spec/routing/session_routing_spec.rb b/spec/routing/session_routing_spec.rb index 58b0801..304a8da 100644 --- a/spec/routing/session_routing_spec.rb +++ b/spec/routing/session_routing_spec.rb @@ -1,7 +1,9 @@ -require 'rails_helper' +# frozen_string_literal: true + +require "rails_helper" describe "Session Routing" do - context 'if user accounts are enabled' do + context "if user accounts are enabled" do before(:all) do Alchemy::Devise.enable_user_accounts = true Rails.application.reload_routes! @@ -9,33 +11,33 @@ it "routes to login" do expect({ - get: "/account/login" + get: "/account/login", }).to route_to( controller: "alchemy/sessions", - action: "new" + action: "new", ) end it "routes to create session" do expect({ - post: "/account/login" + post: "/account/login", }).to route_to( controller: "alchemy/sessions", - action: "create" + action: "create", ) end it "routes to logout" do expect({ - delete: "/account/logout" + delete: "/account/logout", }).to route_to( controller: "alchemy/sessions", - action: "destroy" + action: "destroy", ) end end - context 'if user accounts are disabled' do + context "if user accounts are disabled" do before(:all) do Alchemy::Devise.enable_user_accounts = false Rails.application.reload_routes! @@ -43,28 +45,28 @@ it "does not route to login" do expect({ - get: "/account/login" + get: "/account/login", }).to_not route_to( controller: "alchemy/sessions", - action: "new" + action: "new", ) end it "does not route to create session" do expect({ - post: "/account/login" + post: "/account/login", }).to_not route_to( controller: "alchemy/sessions", - action: "create" + action: "create", ) end it "does not route to logout" do expect({ - delete: "/account/logout" + delete: "/account/logout", }).to_not route_to( controller: "alchemy/sessions", - action: "destroy" + action: "destroy", ) end end