Rails Devise User Model with Roles

Configure devise (for multiple types of accounts)

install the devise engine:

bin/rails generate devise:install

now follow the basic setup config – add to config/environments/development.rb

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

add notifications to the layout for devise in app/views/layouts/application.html.erb just above <%= yeild %>

<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

now create one or more models for devise:

rails g devise:views
rails generate devise user

update the routes to put the login in separate routes in config/routes.rb - make the routes look like:

  devise_for :users,  path: 'users'  # http://localhost:3000/users/sign_in
  devise_for :admins, path: 'admins' # http://localhost:3000/admins/sign_in

turn on scoped views (since login forms can be different) in config/initializers/devise.rb

config.scoped_views = true

Create the scoped views: (instead of: rails g devise:views) do:

rails g devise:views users/devise
rails g devise:views admins/devise

now we should open these migrations and uncomment any added fields we use - I generally like to use most of the fields:

# frozen_string_literal: true

class DeviseCreateAdmins < ActiveRecord::Migration[6.0]
  def change
    create_table :admins do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.inet     :current_sign_in_ip
      t.inet     :last_sign_in_ip

      ## Confirmable
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      t.string   :unlock_token # Only if unlock strategy is :email or :both
      t.datetime :locked_at

      t.timestamps null: false
    end

    add_index :admins, :email,                unique: true
    add_index :admins, :reset_password_token, unique: true
    add_index :admins, :confirmation_token,   unique: true
    add_index :admins, :unlock_token,         unique: true
  end
end

and adjust the user and admin models too and turn on the features we want or need. We will go into detail later, for now I will just add trackable to the models:

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable, :trackable,
         :recoverable, :rememberable, :validatable
end

and of course migrate too.

bin/rails db:migrate

Create custome controllers for each sessions - this also allows the users to have different fields and features:

rails generate devise:controllers users/devise
rails generate devise:controllers admins/devise

configure the routes to point to these new controllers:

  # http://localhost:3000/users/sign_in
  devise_for :users,  path: 'users',
                      controllers: {
                        sessions:      'users/devise/sessions',
                        passwords:     'users/devise/passwords',
                        registrations: 'users/devise/registrations'
                      }
  # http://localhost:3000/admins/sign_in
  devise_for :admins, path: 'admins',
                      controllers: {
                        sessions:      'admins/devise/sessions',
                        passwords:     'admins/devise/passwords',
                        registrations: 'admins/devise/registrations'
                      }

now the routes should look like:

$ bin/rails routes
                     Prefix Verb   URI Pattern                        Controller#Action
           new_user_session GET    /users/sign_in(.:format)           users/sessions#new
               user_session POST   /users/sign_in(.:format)           users/sessions#create
       destroy_user_session DELETE /users/sign_out(.:format)          users/sessions#destroy
          new_user_password GET    /users/password/new(.:format)      users/passwords#new
         edit_user_password GET    /users/password/edit(.:format)     users/passwords#edit
              user_password PATCH  /users/password(.:format)          users/passwords#update
                            PUT    /users/password(.:format)          users/passwords#update
                            POST   /users/password(.:format)          users/passwords#create
   cancel_user_registration GET    /users/cancel(.:format)            user/registrations#cancel
      new_user_registration GET    /users/sign_up(.:format)           user/registrations#new
     edit_user_registration GET    /users/edit(.:format)              user/registrations#edit
          user_registration PATCH  /users(.:format)                   user/registrations#update
                            PUT    /users(.:format)                   user/registrations#update
                            DELETE /users(.:format)                   user/registrations#destroy
                            POST   /users(.:format)                   user/registrations#create
          new_admin_session GET    /admins/sign_in(.:format)          admin/sessions#new
              admin_session POST   /admins/sign_in(.:format)          admin/sessions#create
      destroy_admin_session DELETE /admins/sign_out(.:format)         admin/sessions#destroy
         new_admin_password GET    /admins/password/new(.:format)     admin/passwords#new
        edit_admin_password GET    /admins/password/edit(.:format)    admin/passwords#edit
             admin_password PATCH  /admins/password(.:format)         admin/passwords#update
                            PUT    /admins/password(.:format)         admin/passwords#update
                            POST   /admins/password(.:format)         admin/passwords#create
  cancel_admin_registration GET    /admins/cancel(.:format)           admin/registrations#cancel
     new_admin_registration GET    /admins/sign_up(.:format)          admin/registrations#new
    edit_admin_registration GET    /admins/edit(.:format)             admin/registrations#edit
         admin_registration PATCH  /admins(.:format)                  admin/registrations#update
                            PUT    /admins(.:format)                  admin/registrations#update
                            DELETE /admins(.:format)                  admin/registrations#destroy
                            POST   /admins(.:format)                  admin/registrations#create

lets make logged in home pages (for the user and admin)

rails g controller users/home index --no-helper --no-assets --no-controller-specs --no-view-specs
rails g controller admins/home index --no-helper --no-assets --no-controller-specs --no-view-specs

now lets update our routes to ponit to these pages if the user is logged in add the following belos the deivse_for commands

Rails.application.routes.draw do
  # http://localhost:3000/admins/sign_in
  devise_for :admins, path: 'admins',
                      controllers: {
                        sessions:      'admins/devise/sessions',
                        passwords:     'admins/devise/passwords',
                        registrations: 'admins/devise/registrations'
                      }
  # http://localhost:3000/umdzes/sign_in
  devise_for :umdzes, path: 'umdzes',
                      controllers: {
                        sessions:      'umdzes/devise/sessions',
                        passwords:     'umdzes/devise/passwords',
                        registrations: 'umdzes/devise/registrations'
                      }
  # http://localhost:3000/patrons/sign_in
  devise_for :patrons,  path: 'patrons',
                      controllers: {
                        sessions:      'patrons/devise/sessions',
                        passwords:     'patrons/devise/passwords',
                        registrations: 'patrons/devise/registrations'
                      }

  authenticated :patron do
    root 'patrons/home#index',     as: :auth_patron_root
  end
  authenticated :umdze do
    root 'umdzes/home#index',      as: :auth_umdze_root
  end
  authenticated :admin do
    root 'admins/home#index', as: :auth_admin_root
  end


  namespace :admins do
    get 'home/index'
    # resource  :home_page,        only: [:index]
  end
  get '/admins', to: 'admins/home#index', as: :admins

  namespace :umdzes do
    get 'home/index'
    # resource  :home_page,        only: [:index]
  end
  get '/umdzes', to: 'umdzes/home#index', as: :umdzes

  namespace :patrons do
    get 'home/index'
    # resource  :home_page,        only: [:index]
  end
  get '/patrons', to: 'patrons/home#index', as: :patrons

  get '/landing', to: 'landing#index', as: :landing
  get 'landing/index'
  root to: "landing#index"
end

now lets make ApplicationControllers for each namespace & enforce authentication

touch app/controllers/admins/application_controller.rb
cat << EOF > app/controllers/admins/application_controller.rb
class Admins::ApplicationController < ApplicationController
  before_action :authenticate_admin!

  private

  def this_user
    current_admin
  end
end
EOF

touch app/controllers/umdzes/application_controller.rb
cat << EOF > app/controllers/umdzes/application_controller.rb
class Umdzes::ApplicationController < ApplicationController
  before_action :authenticate_umdze!, unless: :allowed_access

  private

  def allowed_access
    current_admin
  end

  def this_user
    current_umdze || current_admin
  end
end
EOF

touch app/controllers/patrons/application_controller.rb
cat << EOF > app/controllers/patrons/application_controller.rb
class Patrons::ApplicationController < ApplicationController
  before_action :authenticate_patron!, unless: :allowed_access

  private
  def allowed_access
    current_umdze || current_admin
  end

  def this_user
    current_patron || current_umdze || current_admin
  end
end
EOF

now we will inhert from these new controllers and enforce limits

now lets require these pages to have authenticated the correct user type:

# app/controllers/admins/home_controller.rb
class Admins::HomeController < Admins::ApplicationController
  def index
  end
end

# app/controllers/umdzes/home_controller.rb
class Umdzes::HomeController < Umdzes::ApplicationController
  def index
  end
end

# app/controllers/patrons/home_controller.rb
class Patrons::HomeController < Patrons::ApplicationController
  def index
  end
end

Now prevent student and admin accounts from cross visits (during testing, or whatever)

create this new file:

touch app/controllers/concerns/accessible.rb
cat << EOF > app/controllers/concerns/accessible.rb
module Accessible
  extend ActiveSupport::Concern
  included do
    before_action :check_user
  end

  protected
  def check_user
    if current_admin
      flash.clear
      # The authenticated admin root path can be defined in your routes.rb in: devise_scope :admin do...
      redirect_to(auth_admin_root_path) and return
    elsif current_umdze
      flash.clear
      # The authenticated admin root path can be defined in your routes.rb in: devise_scope :admin do...
      redirect_to(auth_umdze_root_path) and return
    elsif current_patron
      flash.clear
      # The authenticated user root path can be defined in your routes.rb in: devise_scope :user do...
      redirect_to(auth_partron_root_path) and return
    end
  end
end
EOF

use this accessible concern

Now add include Accessible in the appropriate controllers:

Note: You must skip_before_action for the destroy action in each SessionsController to prevent the redirect to happen before the sign out occurs.

# eg. ../controllers/admins/sessions_controller.rb
class Admins::SessionsController < Devise::SessionsController
 include Accessible
 skip_before_action :check_user, only: :destroy
 # ...
end

# eg. ../controllers/admins/registrations_controller.rb
You must also skip_before_action for the edit, update, destroy, and cancel actions in each RegistrationsController to allow current users to edit and cancel their own accounts. Otherwise they will be redirected before they can reach these pages.

class Admins::RegistrationsController < Devise::RegistrationsController
 include Accessible
 skip_before_action :check_user, except: [:new, :create]
 # ...
end

# eg. ../controllers/umdzes/sessions_controller.rb
class Umdzes::SessionsController < Devise::SessionsController

 include Accessible
 skip_before_action :check_user, only: :destroy
 # ...
end

# eg. ../controllers/umdzes/registrations_controller.rb
class Umdzes::RegistrationsController < Devise::RegistrationsController

 include Accessible
 skip_before_action :check_user, except: [:new, :create]
 # ...
end

# eg. ../controllers/patrons/sessions_controller.rb
class Patrons::SessionsController < Devise::SessionsController

 include Accessible
 skip_before_action :check_user, only: :destroy
 # ...
end

# eg. ../controllers/patrons/registrations_controller.rb
class Patrons::RegistrationsController < Devise::RegistrationsController

 include Accessible
 skip_before_action :check_user, except: [:new, :create]
 # ...
end

now lets give the patron account a usernames

https://github.com/heartcombo/devise/wiki/How-To%3A-Allow-users-to-sign-in-with-something-other-than-their-email-address

rails generate migration add_username_to_patrons username:string:uniq
rails generate migration add_umdzes_name_to_umdzes fullname:string
rails generate migration add_admins_name_to_admins fullname:string

# now update the new migration to look like:
class AddUsernamToPatrons < ActiveRecord::Migration[6.0]
  def change
    # username is key not email - in fact we don't want an email
    rename_column :patrons, :email, :username
  end
end

class AddFullnameToUmdzes < ActiveRecord::Migration[6.0]
  def change
    add_column :umdzes, :umdzes_name, :string, null: false
  end
end

class AddFullnameToAdmins < ActiveRecord::Migration[6.0]
  def change
    add_column :admins, :admins_name, :string, null: false
  end
end

update the models

now we need to go to the models and make the following updates:

# app/models/admin.rb
class Admin < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise  :database_authenticatable, :trackable, # :registerable,
          :rememberable, :validatable #, :recoverable

  validates :email, uniqueness: true
  validates :admins_name, presence: true
end

# app/models/umdze.rb
class Umdze < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise  :database_authenticatable, :trackable, # :registerable,
          :rememberable, :validatable #, :recoverable

  validates :email, uniqueness: true
  validates :umdzes_name, presence: true
end


# app/models/patrons.rb
class Patron < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise  :database_authenticatable, :trackable, # :registerable,
          :rememberable, :validatable, # :recoverable
          :authentication_keys => [:username]

  validates :username, uniqueness: true
  # make the email field optional
  # validates :email, uniqueness: true

  def email_required?
    false
  end

  def email_changed?
    false
  end

  # use this instead of email_changed? for Rails = 5.1.x
  def will_save_change_to_email?
    false
  end
end

now we can safely migrate bundle exec rails db:migrate

lets test our logins

lets create some common feature test code:

https://forum.upcase.com/t/rspec-support-vs-helpers/4986 https://thoughtbot.com/blog/rspec-integration-tests-with-capybara

# spec/support/features/session_helpers.rb
module Features
  module SessionHelpers
    # def patron_sign_up(username:, password:)
    #   visit new_patron_registration_path
    #   expect(page).to have_button('Sign up')
    #   fill_in 'Username', with: username
    #   fill_in 'Password', with: password
    #   click_button 'Sign up'
    # end
    def patron_log_in(patron = nil)
      patron = FactoryBot.create :patron if patron.nil?
      visit new_patron_session_path
      expect(page).to have_button('Log in')
      fill_in 'Username', with: patron.username
      fill_in 'Password', with: patron.password
      click_on 'Log in'
    end

    # def umdze_sign_up(email:, password:)
    #   visit new_umdze_registration_path
    #   expect(page).to have_button('Sign up')
    #   fill_in 'Email', with: email
    #   fill_in 'Password', with: password
    #   click_button 'Sign up'
    # end
    def umdze_log_in(umdze = nil)
      umdze = FactoryBot.create :umdze if umdze.nil?
      visit new_admin_session_path
      expect(page).to have_button('Log in')
      fill_in 'Email', with: admin.email
      fill_in 'Password', with: admin.password
      click_on 'Log in'
    end

    # def admin_sign_up(email:, password:)
    #   visit new_admin_registration_path
    #   expect(page).to have_button('Sign up')
    #   fill_in 'Email', with: email
    #   fill_in 'Password', with: password
    #   click_button 'Sign up'
    # end
    def admin_log_in(admin = nil)
      admin = FactoryBot.create :admin if admin.nil?
      visit new_admin_session_path
      expect(page).to have_button('Log in')
      fill_in 'Email', with: admin.email
      fill_in 'Password', with: admin.password
      click_on 'Log in'
    end
  end
end

We are not allowing registrations, so that code is commented out. However, we see we must configure our factories for this code to work.

Lets tell rspec how to access this code in feature tests:

# spec/support/features.rb
RSpec.configure do |config|
  config.include Features::SessionHelpers, type: :feature
end

Lets create test for our devise model factories:

# spec/models/patron_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "factory functions" do
    it "generates a valid user" do
      model = FactoryBot.build :user
      expect(model.valid?).to be true
    end
    it "saves a valid user" do
      model = FactoryBot.build :user
      expect(model.save).to be_truthy
    end
  end

  describe "DB settings" do
    it { have_db_index(:email) }
    it { is_expected.to have_db_column(:encrypted_password) }
  end
end

# spec/models/admin_spec.rb
require 'rails_helper'

RSpec.describe Admin, type: :model do
  describe "factory functions" do
    it "generates a valid admin" do
      model = FactoryBot.build :admin
      expect(model.valid?).to be true
    end
    it "saves a valid admin" do
      model = FactoryBot.build :admin
      expect(model.save).to be_truthy
    end
  end

  describe "DB settings" do
    it { have_db_index(:email) }
    it { is_expected.to have_db_column(:encrypted_password) }
  end
end

be sure these fail - run:

rspec spec/models/

Now we need to configure the factories so all is working:

# spec/factories/patrons.rb
FactoryBot.define do
  factory :user do
    sequence(:email)      { |n| "#{Faker::Internet.email}".split('@').join("#{n}@") }
    password              { 'LetM3-InNow' }
    password_confirmation { 'LetM3-InNow' }
    # enable this if using confirmable
    # confirmed_at { Date.today }
  end
end

# spec/factories/umdzes.rb
FactoryBot.define do
  factory :umdze do
    sequence(:email)      { |n| "#{Faker::Internet.email}".split('@').join("#{n}@") }
    password              { 'LetM3-InNow!' }
    password_confirmation { 'LetM3-InNow!' }
    umdzes_name           { "#{Faker::Name.first_name} #{Faker::Name.last_name}" }
    # enable this if using confirmable
    # confirmed_at          { Date.today }
  end
end

# spec/factories/admins.rb
FactoryBot.define do
  factory :admin do
    sequence(:email)      { |n| "#{Faker::Internet.email}".split('@').join("#{n}@") }
    password              { 'LetM3-InNow!' }
    password_confirmation { 'LetM3-InNow!' }
    admins_name           { "#{Faker::Name.first_name} #{Faker::Name.last_name}" }
    # enable this if using confirmable
    # confirmed_at          { Date.today }
  end
end

be sure these pass now - run:

rspec spec/models/

Now we are ready to test devise and our restricted access to the users home page:

https://www.madetech.com/blog/feature-testing-with-rspec https://thoughtbot.com/blog/rspec-integration-tests-with-capybara https://github.com/heartcombo/devise/wiki/How-To:-Test-with-Capybara https://radavis.github.io/sign-in-out-test-helpers-for-and-devise-and-capybara/ https://www.vanderpol.net/2014/10/07/rspec-integration-tests-devise-user-registration/

# spec/features/users/user_signup_spec.rb
require 'rails_helper'

RSpec.describe 'Users Home Page', type: :feature do
  # note user is NOT created in DB!
  let(:user)  { FactoryBot.build :user }
  after :each do
    Warden.test_reset!
  end
  describe 'user is not signed-up' do
    scenario 'user signs-up on registration page' do
      user_sign_up(email: user.email, password: user.password)
      expect(current_path).to eql(users_home_path)
    end
  end
end


# spec/features/users/user_login_spec.rb
require 'rails_helper'

RSpec.describe 'Users Login', type: :feature do
  let(:user)  { FactoryBot.create :user }
  after :each do
    Warden.test_reset!
  end
  describe 'user logs in successfully' do
    scenario 'and is redirected to user home page' do
      user_log_in(user)
      expect(current_path).to eql(auth_user_root_path)
    end
  end
end


# spec/features/users_home_page_spec.rb
require 'rails_helper'

RSpec.describe 'Users Home Page', type: :feature do
  let(:user)    { FactoryBot.create :user }
  after :each do
    # force a logout (clear warden info) after each test
    Warden.test_reset!
  end
  describe 'user is not authenticated' do
    scenario 'user is redirected to user login before access to user home' do
      visit users_home_path
      expect(current_path).to eql(new_user_session_path)
    end
  end
  describe 'user is already authenticated' do
    before    { user_log_in(user) }
    scenario 'user gets direct access to the user homepage' do
      visit users_home_path
      expect(page).to have_current_path(users_home_path)
    end
  end
end

and test to be sure admin can log in too:

# spec/features/admins/admin_login_spec.rb
require 'rails_helper'

RSpec.describe 'Users Login', type: :feature do
  after :each do
    Warden.test_reset!
  end
  scenario 'logs in successfully and is redirected to user home page' do
    admin_log_in
    expect(current_path).to eql(auth_admin_root_path)
  end
end


# spec/features/admins/admin_signup_spec.rb
require 'rails_helper'

RSpec.describe 'Admin Signup', type: :feature do
  # IMPORTANT is NOT created in DB!
  let(:admin)  { FactoryBot.build :admin }
  after :each do
    Warden.test_reset!
  end
  describe 'admin is not signed-up' do
    scenario 'admin registers' do
      admin_sign_up(email: admin.email, password: admin.password)
      expect(page).to have_current_path(admins_home_path)
    end
  end
end


# spec/features/admins/admins_home_spec.rb
require 'rails_helper'

RSpec.describe 'Admins Home', type: :feature do
  let(:admin)  { FactoryBot.create :admin }
  after :each do
    Warden.test_reset!
  end
  describe 'un-authenticated' do
    scenario 'attempts to access admins home page is redirected to user login' do
      visit admins_home_path
      expect(current_path).to eql(new_admin_session_path)
    end
  end
  describe 'already authenticated' do
    before    { admin_log_in(admin) }
    scenario 'gets access to the user homepage' do
      visit admins_home_path
      expect(current_path).to eql(admins_home_path)
    end
  end
end

before we wrap up - we need to fix our request specs - now that we added login restrictions:

# spec/requests/users/home_request_spec.rb
require 'rails_helper'

RSpec.describe "Patron::Homes", type: :request do

  let(:patron)   { FactoryBot.create :patron }

  describe "GET /index" do
    context "NOT logged in" do
      after do
        sign_out patron
      end
      it "home as '/patrons' page is NOT accessible" do
        get "/patrons"
        expect(response).to have_http_status(:redirect)
        # to login
      end
      it "home as 'patron_home_path' page is NOT accessible" do
        get patrons_home_path
        expect(response).to have_http_status(:redirect)
      end
      it "home as 'auth_patron_root_path' page is NOT accessible" do
        get auth_patron_root_path
        expect(response).to have_http_status(:success)
        # here we need page match for different root routes
      end
    end

    context "logged in" do
      before do
        sign_in patron
      end
      after do
        sign_out patron
      end
      it "home as '/patrons' page is accessible" do
        get "/patrons"
        expect(response).to have_http_status(:success)
      end
      it "home as 'patrons_home_path' page is accessible" do
        get patrons_home_path
        expect(response).to have_http_status(:success)
      end
      it "home as 'auth_patron_root_path' page is accessible" do
        get auth_patron_root_path
        expect(response).to have_http_status(:success)
      end
    end
  end
end

# spec/requests/umdze/home_request_spec.rb
require 'rails_helper'

RSpec.describe "Umdze::Homes", type: :request do
  let(:umdze)   { FactoryBot.create :umdze }

  describe "GET /index" do
    context "NOT logged in" do
      after do
        sign_out umdze
      end
      it "home as '/umdzes' page is NOT accessible" do
        get "/umdzes"
        expect(response).to have_http_status(:redirect)
        # to login
      end
      it "home as 'umdzes_home_path' page is NOT accessible" do
        get umdzes_home_path
        expect(response).to have_http_status(:redirect)
      end
      it "home as 'auth_umdze_root_path' page is NOT accessible" do
        get auth_umdze_root_path
        expect(response).to have_http_status(:success)
        # here we need page match for different root routes
      end
    end

    context "logged in" do
      before do
        sign_in umdze
      end
      after do
        sign_out umdze
      end
      it "home as '/umdzes' page is accessible" do
        get "/umdzes"
        expect(response).to have_http_status(:success)
      end
      it "home as 'umdzes_home_path' page is accessible" do
        get umdzes_home_path
        expect(response).to have_http_status(:success)
      end
      it "home as 'auth_umdze_root_path' page is accessible" do
        get auth_umdze_root_path
        expect(response).to have_http_status(:success)
      end
    end
  end
end

# spec/requests/admins/dashboard_request_spec.rb
require 'rails_helper'

RSpec.describe "Admins::Dashboards", type: :request do

  let(:admin)   { FactoryBot.create :admin }

  describe "GET /index" do
    context "NOT logged in" do
      it "home as '/admins' page is NOT accessible" do
        get "/admins"
        expect(response).to have_http_status(:redirect)
      end
      it "home as 'admins_home_path' page is NOT accessible" do
        get admins_home_path
        expect(response).to have_http_status(:redirect)
      end
      it "home as 'auth_admin_root_path' page is NOT accessible" do
        get auth_admin_root_path
        expect(response).to have_http_status(:success)
        # here we need page match for different root routes
      end
    end

    context "logged in" do
      before do
        sign_in admin
      end
      after do
        sign_out admin
      end
      it "home as '/admins' page is accessible" do
        get "/admins"
        expect(response).to have_http_status(:success)
      end
      it "home as 'admins_home_path' page is accessible" do
        get admins_home_path
        expect(response).to have_http_status(:success)
      end
      it "home as 'auth_admin_root_path' page is accessible" do
        get auth_admin_root_path
        expect(response).to have_http_status(:success)
      end
    end
  end
end

run the tests and be sure all is green - if so, now is a good time to make a commit!

git add .
git commit -m "rspec and devise configured and tests green"
Bill Tihen
Bill Tihen
Developer, Data Enthusiast, Educator and Nature’s Friend

very curious – known to explore knownledge and nature