Skip to content
This repository
file 176 lines (159 sloc) 5.694 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
RSpec.configure do |config|
  config.add_setting :infer_base_class_for_anonymous_controllers, :default => true
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)
        if RSpec.configuration.infer_base_class_for_anonymous_controllers?
          base_class ||= controller_class
        end
        base_class ||= defined?(ApplicationController) ? ApplicationController : ActionController::Base

        metadata[:described_class] = Class.new(base_class) do
          def self.name
            root_controller = defined?(ApplicationController) ? ApplicationController : ActionController::Base
            if superclass == root_controller || superclass.abstract?
              "AnonymousController"
            else
              superclass.to_s
            end
          end
        end
        metadata[:described_class].class_eval(&body)

        before do
          @orig_routes = self.routes
          resource_name = @controller.respond_to?(:controller_name) ?
            @controller.controller_name.to_sym : :anonymous
          self.routes = ActionDispatch::Routing::RouteSet.new.tap { |r|
            r.draw { resources resource_name }
          }
        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
Something went wrong with that request. Please try again.