diff --git a/.ebextensions/imagemagick.config b/.ebextensions/imagemagick.config new file mode 100644 index 0000000..cb8155c --- /dev/null +++ b/.ebextensions/imagemagick.config @@ -0,0 +1,3 @@ +packages: + yum: + ImageMagick: [] diff --git a/CHANGELOG.md b/CHANGELOG.md index a60535b..9b260a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Internal pages and file links now friendly forward after sign in - **Admin Changes** - Simplify folder archiving and management for admins +- **Profile Changes** + - Users can now edit their profile and account settings - **Search Changes** - Search now returns more specific results as more search terms are added diff --git a/Gemfile b/Gemfile index b062ec0..9ed2bf4 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem "haml", "~> 5.0.4" gem "jquery-rails", "~> 4.3.3" gem "jquery-ui-rails", "~> 6.0.1" gem "kaminari", "~> 1.1.1" +gem "mini_magick", "~> 4.8.0" gem "pg_search", "~> 2.1.2" # Rails defaults diff --git a/Gemfile.lock b/Gemfile.lock index e14f5e0..d43e39f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -170,6 +170,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2018.0812) mimemagic (0.3.2) + mini_magick (4.8.0) mini_mime (1.0.1) mini_portile2 (2.3.0) minitest (5.11.3) @@ -302,6 +303,7 @@ DEPENDENCIES jquery-ui-rails (~> 6.0.1) kaminari (~> 1.1.1) listen (>= 3.0.5, < 3.2) + mini_magick (~> 4.8.0) pg (>= 0.18, < 2.0) pg_search (~> 2.1.2) puma (~> 3.11) diff --git a/app/controllers/internal_controller.rb b/app/controllers/internal_controller.rb index d2c96c0..2c3d2aa 100644 --- a/app/controllers/internal_controller.rb +++ b/app/controllers/internal_controller.rb @@ -14,7 +14,7 @@ def dashboard # GET /directory def directory - @users = User.current.where(approved: true).order(id: :desc).limit(5) + @users = User.current.where(approved: true).order(id: :desc).limit(20) @key_contacts = User.current.where(key_contact: true) end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb new file mode 100644 index 0000000..69dd03b --- /dev/null +++ b/app/controllers/profiles_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Displays user profile pictures. +class ProfilesController < ApplicationController + before_action :find_user, only: [:picture] + + def picture + profile_picture = if params[:thumb] == "1" + @user&.profile_picture&.thumb + else + @user&.profile_picture + end + + if Rails.env.production? + if profile_picture&.url.present? + redirect_to profile_picture.url(query: { "response-content-disposition" => "inline" }) + else + head :ok + end + else + send_file_if_present profile_picture, disposition: "inline" + end + end + + private + + def find_user + @user = User.current.find_by("LOWER(username) = ?", params[:id].to_s.downcase) + end +end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb new file mode 100644 index 0000000..5b87515 --- /dev/null +++ b/app/controllers/settings_controller.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +class SettingsController < ApplicationController + before_action :authenticate_user! + + layout "layouts/full_page_sidebar" + + # # GET /settings/profile + # def profile + # end + + # PATCH /settings/profile + def update_profile + if current_user.update(profile_params) + redirect_to settings_profile_path, notice: "Profile successfully updated." + else + render :profile + end + end + + # PATCH /settings/complete-profile + def complete_profile + if current_user.update(complete_profile_params) + redirect_to dashboard_path, notice: "Profile successfully updated." + else + redirect_to dashboard_path + end + end + + # PATCH /settings/profile/picture + def update_profile_picture + if current_user.update(profile_picture_params) + redirect_to settings_profile_path, notice: "Profile picture successfully updated." + else + render :profile + end + end + + # # GET /settings/account + # def account + # end + + # PATCH /settings/account + def update_account + if current_user.update(account_params) + redirect_to settings_account_path, notice: "Account successfully updated." + else + render :account + end + end + + # PATCH /settings/password + def update_password + if current_user.valid_password?(params[:user][:current_password]) + if current_user.reset_password(params[:user][:password], params[:user][:password_confirmation]) + bypass_sign_in current_user + redirect_to settings_account_path, notice: "Your password has been changed." + else + render :account + end + else + current_user.errors.add(:current_password, "is invalid") + render :account + end + end + + # # GET /settings/email + # def email + # end + + # PATCH /settings/email + def update_email + if current_user.update(email_params) + redirect_to settings_email_path, notice: I18n.t("devise.confirmations.send_instructions") + else + render :email + end + end + + private + + def complete_profile_params + params.require(:user).permit(:phone, :role, :profile_picture) + end + + def profile_params + params.require(:user).permit(:username, :phone, :role) + end + + def profile_picture_params + params.require(:user).permit(:profile_picture, :remove_profile_picture) + end + + def account_params + params.require(:user).permit(:full_name) + end + + def email_params + params.require(:user).permit(:email, :emails_enabled) + end +end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb new file mode 100644 index 0000000..7451574 --- /dev/null +++ b/app/helpers/profiles_helper.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Helps generate URLs for user profile pictures. +module ProfilesHelper + def profile_picture_tag(user, size: 128, style: nil) + image_tag( + picture_profile_path(user.username, thumb: size <= 64 ? "1" : nil), + alt: "", + class: "rounded img-ignore-selection", + style: "max-height: #{size}px; max-width: #{size}px;" + ) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 412f141..716be01 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,6 +10,9 @@ class User < ApplicationRecord # Callbacks after_commit :new_registration!, on: :create + # Uploaders + mount_uploader :profile_picture, ResizableImageUploader + # Constants ORDERS = { "activity desc" => "(CASE WHEN (users.current_sign_in_at IS NULL) THEN users.created_at ELSE users.current_sign_in_at END) desc", @@ -54,6 +57,9 @@ def self.searchable_attributes belongs_to :site, optional: true # Methods + def incomplete_profile? + role.blank? || phone.blank? || self[:profile_picture].blank? + end def email=(email) super(email.try(:downcase)) diff --git a/app/uploaders/resizable_image_uploader.rb b/app/uploaders/resizable_image_uploader.rb new file mode 100644 index 0000000..d61ef41 --- /dev/null +++ b/app/uploaders/resizable_image_uploader.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# Resizes uploaded images to preset dimensions +class ResizableImageUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick + + # Choose what kind of storage to use for this uploader: + storage Rails.env.production? ? :fog : :file + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + File.join(model.class.to_s.underscore.pluralize, model.id.to_s, mounted_as.to_s) + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + # def default_url + # # For Rails 3.1+ asset pipeline compatibility: + # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_')) + # + # "/images/fallback/" + [version_name, "default.png"].compact.join('_') + # end + + # Process files as they are uploaded: + process resize_to_limit: [256, 256] + + # Create different versions of your uploaded files: + version :thumb do + process resize_to_fill: [64, 64] + end + + # Add a white list of extensions which are allowed to be uploaded. + def extension_whitelist + %w(jpg jpeg gif png) + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + def filename + "image#{File.extname(original_filename)}" if original_filename + end +end diff --git a/app/views/directory/_contact.html.haml b/app/views/directory/_contact.html.haml new file mode 100644 index 0000000..772ab4d --- /dev/null +++ b/app/views/directory/_contact.html.haml @@ -0,0 +1,12 @@ +.key-contact + .key-contact-icon + - if user[:profile_picture] + = profile_picture_tag(user, size: 64) + - else + .px-2= icon("far", "user", class: "fa-3x") + .key-contact-info + .key-contact-name + = user.full_name + .key-contact-role= user.role + .key-contact-email= user.email + .key-contact-phone= user.phone diff --git a/app/views/directory/_profile_form.html.haml b/app/views/directory/_profile_form.html.haml new file mode 100644 index 0000000..0dc4aac --- /dev/null +++ b/app/views/directory/_profile_form.html.haml @@ -0,0 +1,5 @@ += form_with model: user, url: settings_complete_profile_path, method: :patch, local: true do |form| + = render "forms/vertical/text_field", form: form, object: user, key: :role, key_name: "Project role", help_text: "Ex: Project Manager, DSMB Member, Co-Investigator, Study Coordinator, etc." if user.role.blank? + = render "forms/vertical/text_field", form: form, object: user, key: :phone, help_text: "Format: 555-555-5555" if user.phone.blank? + = render "forms/vertical/file_field", form: form, object: user, key: :profile_picture, help_text: "Square pictures work best. Supported file formats JPG, PNG, and GIF." + = render "forms/vertical/submit", form: form, object: user, submit_text: "Update profile", cancel: false diff --git a/app/views/internal/_tabs.html.haml b/app/views/internal/_tabs.html.haml index 706085c..3e4a9f8 100644 --- a/app/views/internal/_tabs.html.haml +++ b/app/views/internal/_tabs.html.haml @@ -23,6 +23,14 @@ - else .d-none.d-sm-inline Docs + - current_page = params[:controller].in?(%w(settings)) + = link_to settings_path, class: "header-tab #{"header-tab-active" if current_page}", data: { object: "#{"suppress-click" if current_page?(settings_path)}" } do + = icon("fas", "cog") + - if current_page + Settings + - else + .d-none.d-sm-inline Settings + - if false - current_page = params[:controller].in?(%w(internal)) && params[:action].in?(%w(reports)) = link_to reports_path, class: "header-tab #{"header-tab-active" if current_page}", data: { object: "#{"suppress-click" if current_page?(reports_path)}" } do diff --git a/app/views/internal/dashboard.html.haml b/app/views/internal/dashboard.html.haml index 655a332..315a636 100644 --- a/app/views/internal/dashboard.html.haml +++ b/app/views/internal/dashboard.html.haml @@ -5,6 +5,13 @@ = render "internal/tabs" - content_for :sidebar, render("internal/sidebar") +.d-flex.flex-column.flex-sm-row.justify-content-around + - if current_user.incomplete_profile? + .dashboard-container.dashboard-container-border-accent{ style: "border-top: 1px solid #ccc;border-right: 1px solid #ccc;border-bottom: 1px solid #ccc;background-color: #f7eaed;width: 600px;max-width: 100%;" } + .mb-3 + %strong Complete your profile + = render "directory/profile_form", user: current_user + .d-flex.flex-column.flex-sm-row.justify-content-between .dashboard-container.flex-fill .mb-3 diff --git a/app/views/internal/directory.html.haml b/app/views/internal/directory.html.haml index e945bb2..7d011f6 100644 --- a/app/views/internal/directory.html.haml +++ b/app/views/internal/directory.html.haml @@ -11,40 +11,21 @@ .key-contacts - @key_contacts.each do |user| - .key-contact - .key-contact-icon= icon("far", "user", class: "fa-3x") - .key-contact-info - .key-contact-name - = user.full_name - .key-contact-role= user.role - .key-contact-email= user.email - .key-contact-phone= user.phone + = render "directory/contact", user: user - if @users.present? .subheading Joined Recently - .dashboard-container.dashboard-table - %table.table.table-striped.table-borderless.table-hover.table-sticky - %col - %col - %col.d-none.d-md-table-column - %thead - %tr - %th Name - %th Email - %th.d-none.d-md-table-cell Role - - - @users.each do |user| - %tr - %td= user.full_name - %td= user.email - %td.d-none.d-md-table-cell= user.role + .key-contacts + - @users.each do |user| + = render "directory/contact", user: user .jumbotron.jumbotron-custom-text.jumbotron-shadow - = icon("far", "comments") - = mail_to ActionMailer::Base.smtp_settings[:email], "Contact us" - if your listed information is incorrect. + = icon("fas", "info-circle") + Update your profile in + = succeed "." do + = link_to "settings", settings_path - elsif false .jumbotron.jumbotron-custom-text diff --git a/app/views/internal/search.html.haml b/app/views/internal/search.html.haml index ab0fa25..a38979e 100644 --- a/app/views/internal/search.html.haml +++ b/app/views/internal/search.html.haml @@ -30,14 +30,7 @@ - case document.searchable.class.to_s - when "User" - user = document.searchable - .key-contact - .key-contact-icon= icon("far", "user", class: "fa-3x") - .key-contact-info - .key-contact-name - = user.full_name - .key-contact-role= user.role - .key-contact-email= user.email - .key-contact-phone= user.phone + = render "directory/contact", user: user - when "Document" - document = document.searchable .dashboard-container.dashboard-container-border-accent.mb-3 diff --git a/app/views/menu/_dropdown_account.html.haml b/app/views/menu/_dropdown_account.html.haml index 44fefef..5e92a56 100644 --- a/app/views/menu/_dropdown_account.html.haml +++ b/app/views/menu/_dropdown_account.html.haml @@ -14,8 +14,10 @@ .dropdown-menu.dropdown-menu-right.dropdown-menu-custom .dropdown-menu-custom-inner - = link_to dashboard_path, class: "dropdown-item" do - Dashboard + = link_to "Dashboard", dashboard_path, class: "dropdown-item" + + = link_to "Settings", settings_path, class: "dropdown-item" + - if false = link_to settings_path, class: "dropdown-item" do @@ -29,16 +31,15 @@ = link_to destroy_user_session_path, method: :delete, class: "dropdown-item" do Sign out +%li.nav-item.d-lg-none{ class: current_page?(settings_path) ? "active" : nil } + = link_to settings_path, class: "nav-link" do + Settings + - if current_page?(settings_path) + %span.sr-only (current) + %li.nav-item %hr -- if false - %li.nav-item.d-lg-none{ class: current_page?(settings_path) ? "active" : nil } - = link_to settings_path, class: "nav-link" do - Settings - - if current_page?(settings_path) - %span.sr-only (current) - - if current_user.admin? %li.nav-item.d-lg-none{ class: current_page?(admin_path) ? "active" : nil } = link_to admin_path, class: "nav-link" do diff --git a/app/views/settings/_sidebar.html.haml b/app/views/settings/_sidebar.html.haml new file mode 100644 index 0000000..3a26b00 --- /dev/null +++ b/app/views/settings/_sidebar.html.haml @@ -0,0 +1,9 @@ +.col-12.col-md-3.col-xl-2.sidebar-wrapper + .sidebar + #sidebar-nav-wrapper.sidebar-nav-wrapper.collapse + .pt-3.py-md-3.mx-3.my-0.ml-md-3.mb-md-3.mr-md-0 + %ul.sidebar-menu.sidebar-menu-shadow + %li.sidebar-menu-header= render "sidebar/toggle", title: "Settings" + %li= link_to "Profile", settings_profile_path, class: current_page?(settings_profile_path) ? "active" : nil + %li= link_to "Account", settings_account_path, class: current_page?(settings_account_path) ? "active" : nil + %li= link_to "Email", settings_email_path, class: current_page?(settings_email_path) ? "active" : nil diff --git a/app/views/settings/account.html.haml b/app/views/settings/account.html.haml new file mode 100644 index 0000000..0f9b56d --- /dev/null +++ b/app/views/settings/account.html.haml @@ -0,0 +1,38 @@ +- @title = "Account Settings" + +- @theme = "default" +- content_for :header, current_user.full_name.presence || current_user.username += render "internal/tabs" +- content_for :sidebar, render("settings/sidebar") + +.card.mb-3 + %h4.card-header + = icon("fas", "user") + Change password + .card-body + = form_with model: current_user, url: settings_update_password_path, method: :patch, local: true do |form| + = render "forms/vertical/password_field", form: form, object: current_user, key: :current_password, key_name: "Old password", autocomplete: "current-password" + = render "forms/vertical/password_field", form: form, object: current_user, key: :password, key_name: "New password", autocomplete: "new-password" + = render "forms/vertical/password_field", form: form, object: current_user, key: :password_confirmation, key_name: "Confirm new password", autocomplete: "new-password" + =# render "forms/vertical/submit", form: form, object: current_user, submit_text: "Update password", cancel: false, alternate_link: link_to("I forgot my password", new_user_password_path) + -# TODO: Allow new_user_password_path to be accessible when logged in. + = render "forms/vertical/submit", form: form, object: current_user, submit_text: "Update password", cancel: false + +%a.anchor-top{ name: "username", id: "username" } +.card.mb-3 + %h4.card-header + = icon("fas", "user") + Change name + .card-body + = form_with model: current_user, url: settings_update_account_path, local: true do |form| + = render "forms/vertical/text_field", form: form, object: current_user, key: :full_name, help_text: "You can change your profile picture #{link_to "here", settings_profile_path(anchor: "picture")}.".html_safe + = render "forms/vertical/submit", form: form, object: current_user, submit_text: "Update name", cancel: false + +- if false + .card + %h4.card-header.text-danger + = icon("fas", "user") + Delete account + .card-body + %p You may delete you account, but this action is PERMANENT. + = link_to "Delete your account", settings_delete_account_path, method: :delete, class: "btn btn-outline-danger", data: { confirm: "Delete your account?" } diff --git a/app/views/settings/email.html.haml b/app/views/settings/email.html.haml new file mode 100644 index 0000000..1fc5d57 --- /dev/null +++ b/app/views/settings/email.html.haml @@ -0,0 +1,16 @@ +- @title = "Email Settings" + +- @theme = "default" +- content_for :header, current_user.full_name.presence || current_user.username += render "internal/tabs" +- content_for :sidebar, render("settings/sidebar") + +.card + %h4.card-header + = icon("fas", "envelope") + Change email + .card-body + = form_with model: current_user, url: settings_update_email_path, method: :patch, local: true do |form| + = render "forms/vertical/email_field", form: form, object: current_user, key: :email + =# render "forms/vertical/check_box", form: form, object: current_user, key: :emails_enabled, key_name: "Enable Emails", help_text: "Allow LOFT-HF to send you emails. You will still receive emails for: (1) Password reset emails" + = render "forms/vertical/submit", form: form, object: current_user, submit_text: "Update email", cancel: false diff --git a/app/views/settings/profile.html.haml b/app/views/settings/profile.html.haml new file mode 100644 index 0000000..3872929 --- /dev/null +++ b/app/views/settings/profile.html.haml @@ -0,0 +1,41 @@ +- @title = "Your Profile" + +- @theme = "default" +- content_for :header, current_user.full_name.presence || current_user.username += render "internal/tabs" +- content_for :sidebar, render("settings/sidebar") + +.card.mb-3 + %h4.card-header + = icon("fas", "user-circle") + Profile + .card-body + = form_with model: current_user, url: settings_update_profile_path, method: :patch, local: true do |form| + = render "forms/vertical/text_field", form: form, object: current_user, key: :username + = render "forms/vertical/text_field", form: form, object: current_user, key: :role, key_name: "Project role", help_text: "Ex: Project Manager, DSMB Member, Co-Investigator, Study Coordinator, etc." + = render "forms/vertical/text_field", form: form, object: current_user, key: :phone, help_text: "Format: 555-555-5555" + = render "forms/vertical/submit", form: form, object: current_user, submit_text: "Update profile", cancel: false + +%a.anchor-top{ name: "picture", id: "picture" } +.card + %h4.card-header + = icon("fas", "user-circle") + Profile picture + .card-body + = form_with model: current_user, url: settings_update_profile_picture_path, method: :patch, local: true do |form| + .d-flex.align-items-center.flex-column.flex-sm-row + .mb-4.mb-sm-0 + = render "forms/vertical/file_field", form: form, object: current_user, key: :profile_picture + = render "forms/vertical/submit", form: form, object: current_user, submit_text: "Upload new picture", cancel: false + .ml-5.key-contact-icon + .d-flex.align-items-center.flex-column.flex-md-row + - if current_user[:profile_picture] + .mb-4.mb-md-0= profile_picture_tag current_user, size: 256 + .ml-3 + = link_to settings_update_profile_picture_path(user: { remove_profile_picture: 1 }), method: :patch, class: "btn btn-sm btn-primary" do + = icon("fas", "trash-alt") + Remove profile picture + + - else + .mb-4.mb-md-0.border.rounded.p-3= icon("far", "user", class: "fa-3x") + .ml-3.text-muted.text-center default profile photo diff --git a/config/routes.rb b/config/routes.rb index e3903bc..a6a8f3a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -44,6 +44,31 @@ get :search end + resources :profiles, only: [] do + member do + get :picture + end + end + + + get :settings, to: redirect("settings/profile") + namespace :settings do + get :profile + patch :update_profile, path: "profile" + patch :complete_profile, path: "complete-profile" + get :profile_picture, path: "profile/picture", to: redirect("settings/profile") + patch :update_profile_picture, path: "profile/picture" + + get :account + patch :update_account, path: "account" + get :password, to: redirect("settings/account") + patch :update_password, path: "password" + delete :destroy, path: "account", as: "delete_account" + + get :email + patch :update_email, path: "email" + end + resources :sites get :coordinating_centers, to: "sites#coordinating_centers", path: "coordinating-centers" diff --git a/db/migrate/20181005190050_add_profile_picture_to_users.rb b/db/migrate/20181005190050_add_profile_picture_to_users.rb new file mode 100644 index 0000000..9ccfda6 --- /dev/null +++ b/db/migrate/20181005190050_add_profile_picture_to_users.rb @@ -0,0 +1,5 @@ +class AddProfilePictureToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :profile_picture, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 910e635..4a70753 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.define(version: 2018_08_17_182235) do +ActiveRecord::Schema.define(version: 2018_10_05_190050) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -143,6 +143,7 @@ t.boolean "key_contact", default: false, null: false t.string "phone" t.integer "site_id" + t.string "profile_picture" t.index ["admin"], name: "index_users_on_admin" t.index ["approved"], name: "index_users_on_approved" t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true diff --git a/test/controllers/profiles_controller_test.rb b/test/controllers/profiles_controller_test.rb new file mode 100644 index 0000000..6c8c33d --- /dev/null +++ b/test/controllers/profiles_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ProfilesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/settings_controller_test.rb b/test/controllers/settings_controller_test.rb new file mode 100644 index 0000000..e69dd7d --- /dev/null +++ b/test/controllers/settings_controller_test.rb @@ -0,0 +1,148 @@ +# frozen_string_literal: true + +require "test_helper" + +# Test user settings pages. +class SettingsControllerTest < ActionDispatch::IntegrationTest + setup do + @regular = users(:regular) + end + + test "should get settings" do + login(@regular) + get settings_url + assert_redirected_to settings_profile_url + end + + test "should get profile for regular user" do + login(@regular) + get settings_profile_url + assert_response :success + end + + test "should not get profile for public user" do + get settings_profile_url + assert_redirected_to new_user_session_url + end + + test "should complete profile" do + login(@regular) + patch settings_complete_profile_url, params: { + user: { + role: "Staff Member", + phone: "555-555-5555" + } + } + @regular.reload + assert_equal "Staff Member", @regular.role + assert_equal "555-555-5555", @regular.phone + assert_equal "Profile successfully updated.", flash[:notice] + assert_redirected_to dashboard_url + end + + test "should update profile" do + login(@regular) + patch settings_update_profile_url, params: { + user: { + username: "regularupdate", + role: "Staff Member", + phone: "555-555-5555" + } + } + @regular.reload + assert_equal "regularupdate", @regular.username + assert_equal "Staff Member", @regular.role + assert_equal "555-555-5555", @regular.phone + assert_equal "Profile successfully updated.", flash[:notice] + assert_redirected_to settings_profile_url + end + + test "should update profile picture" do + login(@regular) + patch settings_update_profile_picture_url, params: { + user: { + profile_picture: fixture_file_upload(file_fixture("rails.png")) + } + } + @regular.reload + assert_equal true, @regular.profile_picture.present? + assert_equal "Profile picture successfully updated.", flash[:notice] + assert_redirected_to settings_profile_url + end + + test "should get account" do + login(@regular) + get settings_account_url + assert_response :success + end + + test "should update account" do + login(@regular) + patch settings_update_account_url, params: { + user: { username: "newusername" } + } + assert_equal "Account successfully updated.", flash[:notice] + assert_redirected_to settings_account_url + end + + test "should update password" do + sign_in_as(@regular, "password") + patch settings_update_password_url, params: { + user: { + current_password: "password", + password: "newpassword", + password_confirmation: "newpassword" + } + } + assert_equal "Your password has been changed.", flash[:notice] + assert_redirected_to settings_account_url + end + + test "should not update password as user with invalid current password" do + sign_in_as(@regular, "password") + patch settings_update_password_url, params: { + user: { + current_password: "invalid", + password: "newpassword", + password_confirmation: "newpassword" + } + } + assert_response :success + end + + test "should not update password with new password mismatch" do + sign_in_as(@regular, "password") + patch settings_update_password_url, params: { + user: { + current_password: "password", + password: "newpassword", + password_confirmation: "mismatched" + } + } + assert_response :success + end + + # test "should delete account" do + # login(@regular) + # assert_difference("User.current.count", -1) do + # delete settings_delete_account_url + # end + # assert_redirected_to root_url + # end + + test "should get email" do + login(@regular) + get settings_email_url + assert_response :success + end + + test "should update email" do + login(@regular) + patch settings_update_email_url, params: { user: { email: "newemail@example.com" } } + @regular.reload + assert_equal "regular@example.com", @regular.email + assert_equal "newemail@example.com", @regular.unconfirmed_email + assert_equal I18n.t("devise.confirmations.send_instructions"), flash[:notice] + assert_redirected_to settings_email_url + end +end diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29..0000000