public
Description: have_tag() without Rails' assert_select()
Clone URL: git://github.com/pd/rspec_hpricot_matchers.git
pd (author)
Mon Mar 10 17:47:05 -0700 2008
commit  54f6522b2c0be754131ce1ee491c6245a34a174d
tree    d9669a12fddb59ea9e24ee56688584c3155e7238
parent  72534eab4d7154f790c97fb35a309e1a5a31cc5f
100644 113 lines (97 sloc) 3.287 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
module RspecHpricotMatchers
  class HaveTag
    def initialize(selector, inner_text_or_options, options, &block)
      @selector = selector
      if Hash === inner_text_or_options
        @inner_text = nil
        @options = inner_text_or_options
      else
        @inner_text = inner_text_or_options
        @options = options
      end
    end
 
    def matches?(actual, &block)
      @actual = actual
      @hdoc = hdoc_for(@actual)
 
      matched_elements = @hdoc.search(@selector)
      if matched_elements.empty?
        return @options[:count] == 0
      end
 
      if @inner_text
        matched_elements = filter_on_inner_text(matched_elements)
      end
 
      if block
        matched_elements = filter_on_nested_expectations(matched_elements, block)
      end
 
      @actual_count = matched_elements.length
      return false if not acceptable_count?(@actual_count)
 
      !matched_elements.empty?
    end
 
    def failure_message
      explanation = @actual_count ? "but found #{@actual_count}" : "but did not"
      "expected\n#{@hdoc.to_s}\nto have #{failure_count_phrase} #{failure_selector_phrase}, #{explanation}"
    end
 
    def negative_failure_message
      explanation = @actual_count ? "but found #{@actual_count}" : "but did"
      "expected\n#{@hdoc.to_s}\nnot to have #{failure_count_phrase} #{failure_selector_phrase}, #{explanation}"
    end
 
    private
      def hdoc_for(input)
        if Hpricot === input
          input
        elsif input.respond_to?(:body)
          Hpricot(input.body)
        else
          Hpricot(input.to_s)
        end
      end
 
      def filter_on_inner_text(elements)
        elements.select do |el|
          next(el.inner_text =~ @inner_text) if @inner_text.is_a?(Regexp)
          el.inner_text == @inner_text
        end
      end
 
      def filter_on_nested_expectations(elements, block)
        elements.select do |el|
          begin
            block.call(el)
          rescue Spec::Expectations::ExpectationNotMetError
            false
          else
            true
          end
        end
      end
 
      def acceptable_count?(actual_count)
        if @options[:count]
          return false unless @options[:count] === actual_count
        end
        if @options[:minimum]
          return false unless actual_count >= @options[:minimum]
        end
        if @options[:maximum]
          return false unless actual_count <= @options[:maximum]
        end
        true
      end
 
      def failure_count_phrase
        if @options[:count]
          "#{@options[:count]} elements matching"
        elsif @options[:minimum] || @options[:maximum]
          count_explanations = []
          count_explanations << "at least #{@options[:minimum]}" if @options[:minimum]
          count_explanations << "at most #{@options[:maximum]}" if @options[:maximum]
          "#{count_explanations.join(' and ')} elements matching"
        else
          "an element matching"
        end
      end
 
      def failure_selector_phrase
        phrase = @selector.inspect
        phrase << (@inner_text ? " with inner text #{@inner_text.inspect}" : "")
      end
  end
 
  def have_tag(selector, inner_text_or_options = nil, options = {}, &block)
    HaveTag.new(selector, inner_text_or_options, options, &block)
  end
end