RSpec.configure do |config| config.add_setting :infer_base_class_for_anonymous_controllers, :default => false end module RSpec::Rails module ControllerExampleGroup extend ActiveSupport::Concern include RSpec::Rails::RailsExampleGroup include ActionController::TestCase::Behavior include RSpec::Rails::ViewRendering include RSpec::Rails::Matchers::RedirectTo include RSpec::Rails::Matchers::RenderTemplate include RSpec::Rails::Matchers::RoutingMatchers include RSpec::Rails::AssertionDelegator.new(ActionDispatch::Assertions::RoutingAssertions) module ClassMethods # @private def controller_class described_class end # Supports a simple DSL for specifying behavior of ApplicationController. # Creates an anonymous subclass of ApplicationController and evals the # `body` in that context. Also sets up implicit routes for this # controller, that are separate from those defined in "config/routes.rb". # # @note Due to Ruby 1.8 scoping rules in anoymous subclasses, constants # defined in `ApplicationController` must be fully qualified (e.g. # `ApplicationController::AccessDenied`) in the block passed to the # `controller` method. Any instance methods, filters, etc, that are # defined in `ApplicationController`, however, are accessible from # within the block. # # @example # # describe ApplicationController do # controller do # def index # raise ApplicationController::AccessDenied # end # end # # describe "handling AccessDenied exceptions" do # it "redirects to the /401.html page" do # get :index # response.should redirect_to("/401.html") # end # end # end # # If you would like to spec a subclass of ApplicationController, call # controller like so: # # controller(ApplicationControllerSubclass) do # # .... # end def controller(base_class = nil, &body) base_class ||= RSpec.configuration.infer_base_class_for_anonymous_controllers? ? controller_class : ApplicationController metadata[:example_group][:described_class] = Class.new(base_class) do def self.name; "AnonymousController"; end end metadata[:example_group][:described_class].class_eval(&body) orig_routes = nil before do orig_routes = self.routes self.routes = ActionDispatch::Routing::RouteSet.new.tap { |r| r.draw { resources :anonymous } } end after do self.routes = orig_routes @orig_routes = nil end end # Specifies the routeset that will be used for the example group. This # is most useful when testing Rails engines. # # @example # # describe MyEngine::PostsController do # routes { MyEngine::Engine.routes } # # # ... # end def routes(&blk) before do self.routes = blk.call end end end attr_reader :controller, :routes # @api private def routes=(routes) @routes = routes assertion_instance.instance_variable_set(:@routes, routes) end module BypassRescue def rescue_with_handler(exception) raise exception end end # Extends the controller with a module that overrides # `rescue_with_handler` to raise the exception passed to it. Use this to # specify that an action _should_ raise an exception given appropriate # conditions. # # @example # # describe ProfilesController do # it "raises a 403 when a non-admin user tries to view another user's profile" do # profile = create_profile # login_as profile.user # # expect do # bypass_rescue # get :show, :id => profile.id + 1 # end.to raise_error(/403 Forbidden/) # end # end def bypass_rescue controller.extend(BypassRescue) end # If method is a named_route, delegates to the RouteSet associated with # this controller. def method_missing(method, *args, &block) if defined?(@routes) && @routes.named_routes.helpers.include?(method) controller.send(method, *args, &block) elsif defined?(@orig_routes) && @orig_routes && @orig_routes.named_routes.helpers.include?(method) controller.send(method, *args, &block) else super end end included do subject { controller } metadata[:type] = :controller before do self.routes = ::Rails.application.routes end around do |ex| previous_allow_forgery_protection_value = ActionController::Base.allow_forgery_protection begin ActionController::Base.allow_forgery_protection = false ex.call ensure ActionController::Base.allow_forgery_protection = previous_allow_forgery_protection_value end end end end end