lib/spec/rails/matchers/assert_select.rb



# This is a wrapper of assert_select for rspec.

module Spec # :nodoc:
  module Rails
    module Matchers

      class AssertSelect #:nodoc:
        
        def initialize(assertion, spec_scope, *args, &block)
          @assertion = assertion
          @spec_scope = spec_scope
          @args = args
          @block = block
        end
        
        def matches?(response_or_text, &block)
          @block = block if block

          if doc = doc_from(response_or_text)
            @args.unshift(doc)
          end

          begin
            @spec_scope.__send__(@assertion, *@args, &@block)
            true
          rescue ::Test::Unit::AssertionFailedError => @error
            false
          end
        end
        
        def failure_message_for_should; @error.message; end
        def failure_message_for_should_not; "should not #{description}, but did"; end

        def description
          {
            :assert_select => "have tag#{format_args(*@args)}",
            :assert_select_email => "send email#{format_args(*@args)}",
          }[@assertion]
        end

      private

        module TestResponseOrString
          def test_response?
            ActionController::TestResponse === self and
                                               !self.headers['Content-Type'].blank? and
                                               self.headers['Content-Type'].to_sym == :xml
          end
        
          def string?
            String === self
          end
        end

        def doc_from(response_or_text)
          response_or_text.extend TestResponseOrString
          markup = response_or_text.body if response_or_text.test_response?
          markup = response_or_text if response_or_text.string?
          HTML::Document.new(markup, false, true).root if markup
        end
        
        def format_args(*args)
          args.empty? ? "" : "(#{arg_list(*args)})"
        end

        def arg_list(*args)
          args.map do |arg|
            arg.respond_to?(:description) ? arg.description : arg.inspect
          end.join(", ")
        end
        
      end
      
      # :call-seq:
      #   response.should have_tag(*args, &block)
      #   string.should have_tag(*args, &block)
      #
      # wrapper for assert_select with additional support for using
      # css selectors to set expectation on Strings. Use this in
      # helper specs, for example, to set expectations on the results
      # of helper methods.
      #
      # == Examples
      #
      #   # in a controller spec
      #   response.should have_tag("div", "some text")
      #
      #   # in a helper spec (person_address_tag is a method in the helper)
      #   person_address_tag.should have_tag("input#person_address")
      #
      # see documentation for assert_select at http://api.rubyonrails.org/
      def have_tag(*args, &block)
        @__current_scope_for_assert_select = AssertSelect.new(:assert_select, self, *args, &block)
      end
    
      # wrapper for a nested assert_select
      #
      #   response.should have_tag("div#form") do
      #     with_tag("input#person_name[name=?]", "person[name]")
      #   end
      #
      # see documentation for assert_select at http://api.rubyonrails.org/
      def with_tag(*args, &block)
        @__current_scope_for_assert_select.should have_tag(*args, &block)
      end
    
      # wrapper for a nested assert_select with false
      #
      #   response.should have_tag("div#1") do
      #     without_tag("span", "some text that shouldn't be there")
      #   end
      #
      # see documentation for assert_select at http://api.rubyonrails.org/
      def without_tag(*args, &block)
        @__current_scope_for_assert_select.should_not have_tag(*args, &block)
      end
    
      # :call-seq:
      #   response.should have_rjs(*args, &block)
      #
      # wrapper for assert_select_rjs
      #
      # see documentation for assert_select_rjs at http://api.rubyonrails.org/
      def have_rjs(*args, &block)
        AssertSelect.new(:assert_select_rjs, self, *args, &block)
      end
      
      # :call-seq:
      #   response.should send_email(*args, &block)
      #
      # wrapper for assert_select_email
      #
      # see documentation for assert_select_email at http://api.rubyonrails.org/
      def send_email(*args, &block)
        AssertSelect.new(:assert_select_email, self, *args, &block)
      end
      
      # wrapper for assert_select_encoded
      #
      # see documentation for assert_select_encoded at http://api.rubyonrails.org/
      def with_encoded(*args, &block)
        should AssertSelect.new(:assert_select_encoded, self, *args, &block)
      end
    end
  end
end