thoughtbot--shoulda-matchers/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb

278 lines
7.6 KiB
Ruby
Raw Normal View History

require 'delegate'
begin
require 'strong_parameters'
rescue LoadError
end
require 'active_support/hash_with_indifferent_access'
module Shoulda
module Matchers
module ActionController
# The `permit` matcher tests that an action in your controller receives a
# whitelist of parameters using Rails' Strong Parameters feature
# (specifically that `permit` was called with the correct arguments).
#
# Here's an example:
#
# class UsersController < ApplicationController
# def create
# user = User.create(user_params)
# # ...
# end
#
# private
#
# def user_params
# params.require(:user).permit(
# :first_name,
# :last_name,
# :email,
# :password
# )
# end
# end
#
# # RSpec
# describe UsersController do
# it do
# should permit(:first_name, :last_name, :email, :password).
# for(:create)
# end
# end
#
# # Test::Unit
# class UsersControllerTest < ActionController::TestCase
# should permit(:first_name, :last_name, :email, :password).
# for(:create)
# end
#
# If your action requires query parameters in order to work, then you'll
# need to supply them:
#
# class UsersController < ApplicationController
# def update
# user = User.find(params[:id])
#
# if user.update_attributes(user_params)
# # ...
# else
# # ...
# end
# end
#
# private
#
# def user_params
# params.require(:user).permit(
# :first_name,
# :last_name,
# :email,
# :password
# )
# end
# end
#
# # RSpec
# describe UsersController do
# before do
# create(:user, id: 1)
# end
#
# it do
# should permit(:first_name, :last_name, :email, :password).
# for(:update, params: { id: 1 })
# end
# end
#
# # Test::Unit
# class UsersControllerTest < ActionController::TestCase
# setup do
# create(:user, id: 1)
# end
#
# should permit(:first_name, :last_name, :email, :password).
# for(:update, params: { id: 1 })
# end
#
# Finally, if you have an action that isn't one of the seven resourceful
# actions, then you'll need to provide the HTTP verb that it responds to:
#
# Rails.application.routes.draw do
# resources :users do
# member do
# put :toggle
# end
# end
# end
#
# class UsersController < ApplicationController
# def toggle
# user = User.find(params[:id])
#
# if user.update_attributes(user_params)
# # ...
# else
# # ...
# end
# end
#
# private
#
# def user_params
# params.require(:user).permit(:activated)
# end
# end
#
# # RSpec
# describe UsersController do
# before do
# create(:user, id: 1)
# end
#
# it do
# should permit(:activated).for(:toggle,
# params: { id: 1 },
# verb: :put
# )
# end
# end
#
# # Test::Unit
# class UsersControllerTest < ActionController::TestCase
# setup do
# create(:user, id: 1)
# end
#
# should permit(:activated).for(:toggle,
# params: { id: 1 },
# verb: :put
# )
# end
#
# @return [StrongParametersMatcher]
#
def permit(*params)
StrongParametersMatcher.new(params).in_context(self)
end
# @private
class StrongParametersMatcher
attr_writer :stubbed_params
def initialize(expected_permitted_params)
@action = nil
@verb = nil
@request_params = {}
@expected_permitted_params = expected_permitted_params
set_double_collection
end
def for(action, options = {})
@action = action
@verb = options.fetch(:verb, default_verb)
@request_params = options.fetch(:params, {})
self
end
def in_context(context)
@context = context
self
end
def description
"permit #{verb.upcase} ##{action} to receive parameters #{param_names_as_sentence}"
end
def matches?(controller)
@controller = controller
ensure_action_and_verb_present!
Doublespeak.with_doubles_activated do
context.__send__(verb, action, request_params)
end
unpermitted_params.empty?
end
def failure_message
"Expected controller to permit #{unpermitted_params.to_sentence}, but it did not."
end
alias failure_message_for_should failure_message
def failure_message_when_negated
"Expected controller not to permit #{verified_permitted_params.to_sentence}, but it did."
end
alias failure_message_for_should_not failure_message_when_negated
protected
attr_reader :controller, :double_collection, :action, :verb,
:request_params, :expected_permitted_params, :context
def set_double_collection
@double_collection =
Doublespeak.double_collection_for(::ActionController::Parameters)
@double_collection.register_stub(:require).to_return { |params| params }
@double_collection.register_proxy(:permit)
end
def actual_permitted_params
double_collection.calls_to(:permit).inject([]) do |all_param_names, call|
all_param_names + call.args
end.flatten
end
def permit_called?
actual_permitted_params.any?
end
def unpermitted_params
expected_permitted_params - actual_permitted_params
end
def verified_permitted_params
expected_permitted_params & actual_permitted_params
end
def ensure_action_and_verb_present!
if action.blank?
raise ActionNotDefinedError
end
if verb.blank?
raise VerbNotDefinedError
end
end
def default_verb
case action
when :create then :post
when :update then RailsShim.verb_for_update
end
end
def param_names_as_sentence
expected_permitted_params.map(&:inspect).to_sentence
end
# @private
class ActionNotDefinedError < StandardError
def message
'You must specify the controller action using the #for method.'
end
end
# @private
class VerbNotDefinedError < StandardError
def message
'You must specify an HTTP verb when using a non-RESTful action. For example: for(:authorize, verb: :post)'
end
end
end
end
end
end