Skip to content

Commit

Permalink
Add request helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
stephannv committed Nov 20, 2022
1 parent 104e05d commit 45250a1
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 58 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,41 @@ To Generate the clearance specs, run:
rails generate clearance:specs
```

### Request Test Helpers

To test routes that are protected by `before_action :require_login`,
require Clearance's test helpers in your test suite.

For `rspec`, add the following line to your `spec/rails_helper.rb` or
`spec/spec_helper` if `rails_helper` does not exist:

```ruby
require "clearance/rspec"
```

This will make available helper methods to assist you in your request tests.

```ruby
sign_in
sign_in_as(user, password: "12345")
sign_out
```

By default, these helpers will use `session_path` and `sign_out_path` to sign
in and sign out user, but if you are using custom paths, you are able to
provide your paths:

```ruby
sign_in_as(user, password: "12345", path: custom_sign_in_path)
sign_out(user, password: "12345", path: custom_sign_out_path)
```

If you need the reference for signed in user, you can assign the `sign_in`
return to a variable:

```ruby
user = sign_in
```
### Controller Test Helpers

To test controller actions that are protected by `before_action :require_login`,
Expand Down
2 changes: 2 additions & 0 deletions lib/clearance/rspec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
require "rspec/rails"
require "clearance/testing/deny_access_matcher"
require "clearance/testing/controller_helpers"
require "clearance/testing/request_helpers"
require "clearance/testing/view_helpers"

RSpec.configure do |config|
config.include Clearance::Testing::Matchers, type: :controller
config.include Clearance::Testing::ControllerHelpers, type: :controller
config.include Clearance::Testing::RequestHelpers, type: :request
config.include Clearance::Testing::ViewHelpers, type: :view
config.include Clearance::Testing::ViewHelpers, type: :helper

Expand Down
28 changes: 7 additions & 21 deletions lib/clearance/testing/controller_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
require "clearance/testing/utils"

module Clearance
module Testing
# Provides helpers to your controller specs.
# These are typically used in tests by requiring `clearance/rspec` or
# `clearance/test_unit` as appropriate in your `rails_helper.rb` or
# `test_helper.rb` files.
module ControllerHelpers
include Clearance::Testing::Utils

# @api private
def setup_controller_request_and_response
super
@request.env[:clearance] = Clearance::Session.new(@request.env)
end

# Signs in a user that is created using FactoryGirl.
# Signs in a user that is created using FactoryBot or FactoryGirl.
# The factory name is derrived from your `user_class` Clearance
# configuration.
#
# @raise [RuntimeError] if FactoryGirl is not defined.
# @raise [RuntimeError] if FactoryBot or FactoryGirl is not defined.
def sign_in
constructor = factory_module("sign_in")

factory = Clearance.configuration.user_model.to_s.underscore.to_sym
sign_in_as constructor.create(factory)
sign_in_as create_user
end

# Signs in the provided user.
Expand All @@ -37,21 +38,6 @@ def sign_in_as(user)
def sign_out
@request.env[:clearance].sign_out
end

# Determines the appropriate factory library
#
# @api private
# @raise [RuntimeError] if both FactoryGirl and FactoryBot are not
# defined.
def factory_module(provider)
if defined?(FactoryBot)
FactoryBot
elsif defined?(FactoryGirl)
FactoryGirl
else
raise("Clearance's `#{provider}` helper requires factory_bot")
end
end
end
end
end
44 changes: 44 additions & 0 deletions lib/clearance/testing/request_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "clearance/testing/utils"

module Clearance
module Testing
# Provides helpers to your request specs.
# These are typically used in tests by requiring `clearance/rspec` or
# `clearance/test_unit` as appropriate in your `rails_helper.rb` or
# `test_helper.rb` files.
module RequestHelpers
include Clearance::Testing::Utils

# Signs in a user that is created using FactoryBot or FactoryGirl.
# The factory name is derrived from your `user_class` Clearance
# configuration.
#
# @raise [RuntimeError] if FactoryBot or FactoryGirl is not defined.
def sign_in
sign_in_as create_user(password: "password"), password: "password"
end

# Signs in the provided user.
#
# @param [User class] user
# @param [String] password
# @param [String] path

# @return user
def sign_in_as(user, password:, path: session_path)
post path, params: { session: { email: user.email, password: password } }

user
end

# Signs out a user that may be signed in.
#
# @param [String] path
#
# @return [void]
def sign_out(path: sign_out_path)
delete path
end
end
end
end
35 changes: 35 additions & 0 deletions lib/clearance/testing/utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Clearance
module Testing
module Utils
private

# Creates a user using FactoryBot or FactoryGirl.
# The factory name is derrived from `user_class` Clearance
# configuration.
#
# @api private
# @raise [RuntimeError] if FactoryBot or FactoryGirl is not defined.
def create_user(**factory_options)
constructor = factory_module("sign_in")

factory = Clearance.configuration.user_model.to_s.underscore.to_sym
constructor.create(factory, **factory_options)
end

# Determines the appropriate factory library
#
# @api private
# @raise [RuntimeError] if both FactoryGirl and FactoryBot are not
# defined.
def factory_module(provider)
if defined?(FactoryBot)
FactoryBot
elsif defined?(FactoryGirl)
FactoryGirl
else
raise("Clearance's `#{provider}` helper requires factory_bot")
end
end
end
end
end
79 changes: 79 additions & 0 deletions spec/clearance/testing/request_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require "spec_helper"

class SecretsController < ActionController::Base
include Clearance::Controller

before_action :require_login

def index
render json: { status: :ok }
end
end

describe "Secrets managament", type: :request do
before do
Rails.application.routes.draw do
instance_eval(File.read(Rails.root.join("config/routes.rb"))) # drawing clearance routes
post "/custom_sign_in" => "clearance/sessions#create", as: "custom_sign_in"
resources :secrets, only: :index
end
end

describe "GET #index" do
context "with default authenticated user" do
it "renders action response" do
sign_in

get secrets_path

expect(response.body).to eq({ status: :ok }.to_json)
end
end

context "with custom authenticated user" do
it "renders action response" do
user = create(:user, password: "my-password")
sign_in_as(user, password: "my-password")

get secrets_path

expect(response.body).to eq({ status: :ok }.to_json)
end
end

context "with custom sign in path" do
it "renders action response" do
user = create(:user, password: "my-password")
sign_in_as(user, password: "my-password", path: custom_sign_in_path)

get secrets_path

expect(response.body).to eq({ status: :ok }.to_json)
end
end

context "without authenticated user" do
it "redirects to sign in" do
get secrets_path

expect(response).to redirect_to(sign_in_path)
end
end

context "with signed out user" do
it "redirects to sign in" do
sign_in

get secrets_path

expect(response.body).to eq({ status: :ok }.to_json)

sign_out

get secrets_path

expect(response).to redirect_to(sign_in_path)
end
end
end
end
10 changes: 1 addition & 9 deletions spec/requests/authentication_cookie_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def public
describe "Authentication cookies in the response" do
before do
draw_test_routes
create_user_and_sign_in
sign_in
end

after do
Expand All @@ -44,12 +44,4 @@ def draw_test_routes
resource :session, controller: "clearance/sessions", only: [:create]
end
end

def create_user_and_sign_in
user = create(:user, password: "password")

post session_path, params: {
session: { email: user.email, password: "password" },
}
end
end
18 changes: 2 additions & 16 deletions spec/requests/cookie_options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@
context "when httponly config value is false" do
let(:httponly) { false }
describe "sign in" do
before do
user = create(:user, password: "password")
get sign_in_path

post session_path, params: {
session: { email: user.email, password: "password" },
}
end
before { sign_in }

it { should_have_one_remember_token }

Expand All @@ -28,14 +21,7 @@
context "when httponly config value is true" do
let(:httponly) { true }
describe "sign in" do
before do
user = create(:user, password: "password")
get sign_in_path

post session_path, params: {
session: { email: user.email, password: "password" },
}
end
before { sign_in }

it { should_have_one_remember_token }

Expand Down
14 changes: 2 additions & 12 deletions spec/requests/token_expiration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
describe "after signing in" do
before do
Timecop.freeze
create_user_and_sign_in
sign_in
@initial_cookies = remember_token_cookies
end

Expand All @@ -22,7 +22,7 @@

describe "after signing in and making a followup request" do
before do
create_user_and_sign_in
sign_in
@initial_cookies = remember_token_cookies

Timecop.travel(1.minute.from_now) do
Expand All @@ -46,14 +46,4 @@ def first_cookie
def second_cookie
Rack::Test::Cookie.new @followup_cookies.last
end

def create_user_and_sign_in
user = create(:user, password: "password")

get sign_in_path

post session_path, params: {
session: { email: user.email, password: "password" },
}
end
end

0 comments on commit 45250a1

Please sign in to comment.