rspec-tag_matchers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,221 @@
1
+ require 'nokogiri'
2
+
3
+ module RSpec::TagMatchers
4
+ # Matches HTML tags by name.
5
+ #
6
+ # @modifier with_attribute
7
+ # Adds a criteria that an element must match the given attributes.
8
+ #
9
+ # @modifier with_criteria
10
+ # Adds an arbitrary criteria.
11
+ #
12
+ # @example Matching anchor tags
13
+ # it { should have_tag(:a) }
14
+ #
15
+ # @example Matching anchor tags that link to "/"
16
+ # it { should have_tag(:a).with_attribute(:href => "/") }
17
+ #
18
+ # @example Matching anchor tags with an even +id+ attribute
19
+ # it { should have_tag(:a).with_criteria { |elem| elem[:id].to_i.even? } }
20
+ #
21
+ # @return [HasTag]
22
+ #
23
+ # @see HasTag#with_attribute
24
+ # @see HasTag#with_criteria
25
+ def have_tag(name)
26
+ HasTag.new(name)
27
+ end
28
+
29
+ # The base class for all tag matchers. HasTag is intended to provide facilities that are useful to
30
+ # subclasses. The subclasses of HasTag should define more expressive tag matchers. For example, to
31
+ # match a checkbox using HasTag directly, one would have to type:
32
+ #
33
+ # it { has_tag(:input).with_attribute(:type => :checkbox) }
34
+ #
35
+ # That can all be encapsulated into a subclass that provides a more expressive matcher:
36
+ #
37
+ # it { has_checkbox }
38
+ #
39
+ # == Element Matching
40
+ #
41
+ # The way tag matchers work is by counting how many elements match a set of filters. It starts by
42
+ # finding element that match the given tag name, e.g., +a+, +div+, or +input+, and then filters
43
+ # the list of matching elements according to a list of criteria. In the end, the matcher is left
44
+ # with a set of elements that match *all* criteria. The matcher is said to match the input string
45
+ # if the set of elements that match all its criteria contains at least one element. The matched
46
+ # elements need not be the top-level element.
47
+ #
48
+ # === Example
49
+ #
50
+ # In this example, the matcher looks for +div+ elements with and +id+ of "foo" and a class of
51
+ # "bar". An element must match all three of those criteria in order for the matcher to consider it
52
+ # a successful match:
53
+ #
54
+ # matcher = HasTag.new(:div).with_attribute(:id => :foo, :class => :bar)
55
+ # matcher.matches?('<div id="foo" class="bar"></div>') # => true
56
+ # matcher.matches?('<div id="foo"></div>') # => false
57
+ #
58
+ # However, the all criteria must be matched by <strong>the same</strong> element. If one element
59
+ # matches half of the criteria and another element matches the other half of the criteria, it is
60
+ # not considered a successful match:
61
+ #
62
+ # matcher = HasTag.new(:div).with_attribute(:id => :foo, :class => :bar)
63
+ # matcher.matches?('<div id="foo"></div><div class="bar"></div>') # => false
64
+ #
65
+ # == Subclassing HasTag
66
+ #
67
+ # In the most basic case, a subclass of HasTag will simply override the constructor to provide a
68
+ # default tag name that must be matched. For example, a matcher to match +object+ tags might look
69
+ # like this:
70
+ #
71
+ # class HasObject < HasTag
72
+ # def initialize
73
+ # super(:object)
74
+ # end
75
+ # end
76
+ #
77
+ # Also, one should provide a helper method to construct the matcher inside of a spec. For the
78
+ # above example, the helper method would look like this:
79
+ #
80
+ # def have_object
81
+ # HasObject.new
82
+ # end
83
+ #
84
+ # This allows the user to construct a HasObject matcher by calling +have_object+ in his spec,
85
+ # which provides for a more readable spec:
86
+ #
87
+ # it { should have_object }
88
+ #
89
+ # In some cases it might make sense to add additional criteria from within the constructor or to
90
+ # provide additional methods that can be chained from the matcher to provide tag-specific
91
+ # criteria. See {HasTag#with_criteria} for how to add custom criteria to a matcher.
92
+ class HasTag
93
+
94
+ # Constructs a matcher that matches HTML tags by +name+.
95
+ #
96
+ # @param [String, Symbol] name The type of tag to match.
97
+ def initialize(name)
98
+ @name = name.to_s
99
+ @attributes = {}
100
+ @criteria = []
101
+ end
102
+
103
+ # Answers whether or not the matcher matches any elements within +rendered+.
104
+ #
105
+ # @param [String] rendered A string of HTML or an Object whose +to_s+ method returns HTML.
106
+ # (That includes Nokogiri classes.)
107
+ #
108
+ # @return [Boolean]
109
+ def matches?(rendered)
110
+ Nokogiri::HTML::Document.parse(rendered.to_s).css(@name).select do |element|
111
+ matches_attributes?(element) && matches_criteria?(element)
112
+ end.length > 0
113
+ end
114
+
115
+ # Adds a constraint that the matched elements must match certain attributes. The +attributes+
116
+ # hash contains a set of key/value pairs. Each key is used for the attribute name (not case
117
+ # sensitive) and the value is used to determine if an attribute matches according to the rules
118
+ # in the following table:
119
+ #
120
+ # [Attribute Matching Values]
121
+ # String:: The attribute's value must match exactly.
122
+ # Symbol:: The attribute's value must match, but it is not case sensitive.
123
+ # Regexp:: The attribute's value must match the regular expression.
124
+ # true:: The attribute must exist. The attribute's value does not matter.
125
+ # false:: The attribute must *not* exist.
126
+ #
127
+ # @param [Hash] attributes A hash of attribute key/value pairs. The keys are the attribute
128
+ # names.
129
+ #
130
+ # @return [self]
131
+ def with_attribute(attributes)
132
+ @attributes.merge!(attributes)
133
+ self
134
+ end
135
+ alias :with_attributes :with_attribute
136
+
137
+ # Adds an arbitrary criteria to the matched elements. The criteria can be a method name or a
138
+ # block. The method or block should accept a single Nokogiri::XML::Node object as its argument
139
+ # and return whether or not the element passed as an argument matches the criteria.
140
+ #
141
+ # @example
142
+ # have_div.with_criteria { |element| element[:id].to_i % 2 == 0 }
143
+ #
144
+ # @param [Symbol] method The name of the method to be called.
145
+ # @param [Proc] block A block to be calle => d.
146
+ #
147
+ # @return [self]
148
+ def with_criteria(method = nil, &block)
149
+ @criteria << method unless method.nil?
150
+ @criteria << block if block_given?
151
+ self
152
+ end
153
+
154
+ protected
155
+
156
+ # Tests with +attribute+ matches +expected+ according the the attribute matching rules described
157
+ # in {#with_attribute}. This can be useful for testing attributes in subclasses.
158
+ #
159
+ # @note
160
+ # The reason this method receives a +Nokogiri::XML::Attr+ object instead of the attribute's
161
+ # value is that some attributes have meaning merely by existing, even if they don't have a
162
+ # value. For example, the +checked+ attribute of a checkbox or radio button does not need to
163
+ # have a value. If it doesn't have a value, +element[:checked]+ will return +nil+ in the JRuby
164
+ # implementation of Nokogiri, which would make <tt>with_attribute(:checked => true)</tt> fail
165
+ # in JRuby.
166
+ #
167
+ # @param [Nokogiri::XML::Attr] attribute The attribute to be tested.
168
+ # @param [String, Symbol, Regexp, Boolean] expected The expected value of +attribute+.
169
+ #
170
+ # @return [Boolean]
171
+ #
172
+ # @see #with_attribute
173
+ def test_attribute(attribute, expected)
174
+ actual = attribute && attribute.value
175
+
176
+ case expected
177
+ when String
178
+ actual == expected
179
+ when Symbol
180
+ actual =~ /^#{expected}$/i
181
+ when Regexp
182
+ actual =~ expected
183
+ when true
184
+ !attribute.nil?
185
+ when false
186
+ attribute.nil?
187
+ end
188
+ end
189
+
190
+ private
191
+
192
+
193
+ # Answers whether or not +element+ matches the attributes in the attributes hash given to
194
+ # {#with_attribute}.
195
+ #
196
+ # @param [Nokogiri::XML::Node] element The element to be tested.
197
+ #
198
+ # @return [Boolean]
199
+ def matches_attributes?(element) # :api: public
200
+ @attributes.all? do |key, value|
201
+ test_attribute(element.attribute(key.to_s), value)
202
+ end
203
+ end
204
+
205
+ # Answers whether or not +element+ matches the custom criteria set by {#with_criteria}.
206
+ #
207
+ # @param [Nokogiri::XML::Node] element The element to be tested.
208
+ #
209
+ # @return [Boolean]
210
+ def matches_criteria?(element)
211
+ @criteria.all? do |method|
212
+ case method
213
+ when Symbol
214
+ send(method, element)
215
+ when Proc
216
+ method.call(element)
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,22 @@
1
+ require 'rspec/tag_matchers/multiple_input_matcher'
2
+
3
+ module RSpec::TagMatchers
4
+ # Matches inputs generated by Rails' +time_select+ helper.
5
+ #
6
+ # @example Matching a time select for an event's +start_time+
7
+ # it { should have_time_select.for(:event => :start_time) }
8
+ #
9
+ # @return [HasTimeSelect]
10
+ def have_time_select
11
+ HasTimeSelect.new
12
+ end
13
+
14
+ # A matcher that matches Rails' +time_select+ drop-downs.
15
+ class HasTimeSelect < MultipleInputMatcher
16
+
17
+ # Initializes a HasTimeSelect matcher.
18
+ def initialize
19
+ super(4 => HasSelect.new, 5 => HasSelect.new)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+ module RSpec::TagMatchers
2
+ # A matcher that matches multiple input elements. It is intended to serve as a base class for
3
+ # building more specific matchers that must match multiple input elements. For example, a matcher
4
+ # to test for Rails' +time_select+ drop-downs must test that the HTML contains a drop-down for
5
+ # hours and another drop-down for minutes.
6
+ #
7
+ # @example Building a date matcher
8
+ # matcher = MultipleInputMatcher.new(
9
+ # 1 => HasSelect.new,
10
+ # 2 => HasSelect.new,
11
+ # 3 => HasSelect.new
12
+ # )
13
+ # matcher.for(:user => :birthday) # will match <select> tags with names
14
+ # # "user[birthday(1i)]", "user[birthday(2i)]", and
15
+ # # "user[birthday(3i)]"
16
+ #
17
+ # @example Building a time matcher
18
+ # MultipleInputMatcher.new( # by default, will match <select> tags with names by regular
19
+ # 4 => HasSelect.new, # expressions: /\(4i\)/ and /\(5i\)/
20
+ # 5 => HasSelect.new
21
+ # )
22
+ #
23
+ #
24
+ class MultipleInputMatcher
25
+
26
+ # Initializes a matcher that matches multiple input elements.
27
+ #
28
+ # @param [Hash] components A hash of matchers. The keys serve as indices and the values are the
29
+ # matchers that must be satisfied.
30
+ def initialize(components)
31
+ @components = components
32
+ @components.each do |index, matcher|
33
+ matcher.with_attribute(:name => /\(#{index}i\)/)
34
+ end
35
+ end
36
+
37
+ # Tests whether the matcher matches the +rendered+ string. It delegates matching to its
38
+ # matchers. It returns true if all of its matchers return true. It returns false if *any* of its
39
+ # matchers return false.
40
+ #
41
+ # @param [String] rendered A string of HTML or an Object whose +to_s+ method returns HTML.
42
+ # (That includes Nokogiri classes.)
43
+ #
44
+ # @return [Boolean]
45
+ def matches?(rendered)
46
+ @failures = matchers.reject do |matcher|
47
+ matcher.matches?(rendered)
48
+ end
49
+
50
+ @failures.empty?
51
+ end
52
+
53
+ # Specifies the inputs names with more accuracy than the default regular expressions. It
54
+ # delegates to each matcher's +for+ method. But it first appends the matcher's index to the last
55
+ # component of the input's name.
56
+ #
57
+ # @example Input naming delegation
58
+ # hour_matcher = HasSelect.new
59
+ # minute_matcher = HasSelect.new
60
+ # time_matcher = MultipleInputMatcher.new(4 => hour_matcher, 5 => minute_matcher)
61
+ #
62
+ # time_matcher.for(:event => :start_time) # calls hour_matcher.for("event", "start_time(4i)")
63
+ # # and minute_matcher.for("event", "start_time(5i)")
64
+ #
65
+ # @param [Array, Hash] args A hierarchy of string that specify the attribute name.
66
+ #
67
+ # @return [MultipleInputMatcher] self
68
+ def for(*args)
69
+ @components.each do |index, matcher|
70
+ delegated_for(index, matcher, args)
71
+ end
72
+ self
73
+ end
74
+
75
+ # Returns the failure messages from each failed matcher.
76
+ #
77
+ # @return [String] Failure message
78
+ def failure_message
79
+ @failures.map(&:failure_message).join(" and ")
80
+ end
81
+
82
+ # Returns the negative failure messages from every matcher.
83
+ #
84
+ # @return [String] Negative failure message
85
+ def negative_failure_message
86
+ matchers.map(&:negative_failure_message).join(" and ")
87
+ end
88
+
89
+ private
90
+
91
+ # Returns all the matchers.
92
+ #
93
+ # @return [Array] Matchers
94
+ def matchers
95
+ @components.values
96
+ end
97
+
98
+ # Set's +matcher+'s input name according to +args+ and +index+.
99
+ #
100
+ # @param [Integer] index The matcher's index.
101
+ # @param [HasInput] matcher The matcher.
102
+ # @param [Arrah, Hash] args A hierarchy of names that would normally be passed to
103
+ # {HasInput#for}.
104
+ def delegated_for(index, matcher, args)
105
+ args = args.dup
106
+ args.extend(DeepFlattening)
107
+ args = args.deep_flatten
108
+
109
+ args[-1] = args[-1].to_s
110
+ args[-1] += "(#{index}i)"
111
+ matcher.for(*args)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'core_ext'))
2
+
3
+ module RSpec
4
+ module TagMatchers
5
+ end
6
+ end
7
+
8
+ require 'rspec/tag_matchers/has_tag'
9
+ require 'rspec/tag_matchers/has_form'
10
+ require 'rspec/tag_matchers/has_input'
11
+ require 'rspec/tag_matchers/has_checkbox'
12
+ require 'rspec/tag_matchers/has_select'
13
+ require 'rspec/tag_matchers/has_time_select'
@@ -0,0 +1 @@
1
+ require 'rspec/tag_matchers'
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec::Matchers.define :flatten_to do |expected|
4
+ match do |subject|
5
+ actual = subject.deep_flatten
6
+
7
+ if actual == expected
8
+ if !expected.respond_to?(:deep_flatten)
9
+ true
10
+ else
11
+ @failure_message = "expected #{actual.inspect} to not respond to #deep_flatten"
12
+ false
13
+ end
14
+ else
15
+ @failure_message = "expected #{subject.inspect} to flatten to #{expected.inspect}, but flattened to #{actual.inspect}"
16
+ false
17
+ end
18
+ end
19
+
20
+ failure_message do
21
+ @failure_message
22
+ end
23
+ end
24
+
25
+ describe DeepFlattening do
26
+ def extended(object)
27
+ object.extend(DeepFlattening)
28
+ object
29
+ end
30
+
31
+ describe Array do
32
+ context "when not extended" do
33
+ subject { Array.new }
34
+ it { should_not respond_to(:deep_flatten) }
35
+ end
36
+
37
+ context "when extended" do
38
+ subject { extended([]) }
39
+ it { should respond_to(:deep_flatten) }
40
+
41
+ context "[]" do
42
+ subject { extended([]) }
43
+ it { should flatten_to([]) }
44
+ end
45
+
46
+ context "[1,2,3]" do
47
+ subject { extended([1,2,3]) }
48
+ it { should flatten_to([1,2,3]) }
49
+ end
50
+
51
+ context "[[1,2,3]]" do
52
+ subject { extended([[1,2,3]]) }
53
+ it { should flatten_to([1,2,3]) }
54
+ end
55
+
56
+ context "[[1], [2], [3]]" do
57
+ subject { extended([[1], [2], [3]]) }
58
+ it { should flatten_to([1,2,3]) }
59
+ end
60
+
61
+ context "[1, [2, [3, [4]]]]" do
62
+ subject { extended([1, [2, [3, [4]]]]) }
63
+ it { should flatten_to([1,2,3,4]) }
64
+ end
65
+ end
66
+ end
67
+
68
+ describe Hash do
69
+ context "when not extended" do
70
+ subject { Hash.new }
71
+ it { should_not respond_to(:deep_flatten) }
72
+ end
73
+
74
+ context "when extended" do
75
+ subject { extended(Hash.new) }
76
+ it { should respond_to(:deep_flatten) }
77
+
78
+ context "{}" do
79
+ subject { extended({}) }
80
+ it { should flatten_to([]) }
81
+ end
82
+
83
+ context "{:foo => :bar}" do
84
+ subject { extended({:foo => :bar}) }
85
+ it { should flatten_to([:foo, :bar]) }
86
+ end
87
+
88
+ context "{:foo => {:bar => :baz}}" do
89
+ subject { extended({:foo => {:bar => :baz}}) }
90
+ it { should flatten_to([:foo, :bar, :baz]) }
91
+ end
92
+
93
+ context "{:foo => {bar => {:baz => :qux}}}" do
94
+ subject { extended({:foo => {:bar => {:baz => :qux}}}) }
95
+ it { should flatten_to([:foo, :bar, :baz, :qux]) }
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "Mixed data structure" do
101
+ context "when extended" do
102
+ context "[1, {:foo => :bar}]" do
103
+ subject { extended([1, {:foo => :bar}]) }
104
+ it { should flatten_to([1, :foo, :bar]) }
105
+ end
106
+
107
+ context "{:foo => [1,2]}" do
108
+ subject { extended({:foo => [1,2]}) }
109
+ it { should flatten_to([:foo, 1, 2]) }
110
+ end
111
+
112
+ context "[1, {:foo => [2,3]}]" do
113
+ subject { extended([1, {:foo => [2,3]}]) }
114
+ it { should flatten_to([1, :foo, 2, 3]) }
115
+ end
116
+
117
+ context "{:foo => {:bar => [1,2]}}" do
118
+ subject { extended({:foo => {:bar => [1,2]}}) }
119
+ it { should flatten_to([:foo, :bar, 1, 2]) }
120
+ end
121
+
122
+ context "[1, {:foo => [2, {:bar => {:baz => [3,4]}}, 5]}]" do
123
+ subject { extended([1, {:foo => [2, {:bar => {:baz => [3,4]}}, 5]}]) }
124
+ it { should flatten_to([1, :foo, 2, :bar, :baz, 3, 4, 5]) }
125
+ end
126
+
127
+ context "{:foo => {:bar => [1, {:baz => :qux}, 2]}}" do
128
+ subject { extended({:foo => {:bar => [1, {:baz => :qux}, 2]}}) }
129
+ it { should flatten_to([:foo, :bar, 1, :baz, :qux, 2]) }
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::TagMatchers::HasCheckbox do
4
+ include RSpec::TagMatchers
5
+
6
+ describe "matching checkbox tags" do
7
+ context "have_checkbox" do
8
+ subject { have_checkbox }
9
+ it { should_not match("<input />") }
10
+ it { should match("<input type='checkbox' />") }
11
+ it { should match("<input TYPE='CHECKBOX' />") }
12
+ it { should_not match("<input type='text' />") }
13
+ it { should_not match("<input type='checkboxer' />") }
14
+ it { should_not match("input checkbox") }
15
+ end
16
+ end
17
+
18
+ describe "matching checkbox state" do
19
+ context "have_checkbox.checked" do
20
+ subject { have_checkbox.checked }
21
+ it { should_not match("<input type='checkbox' />") }
22
+ it { should match("<input type='checkbox' checked />") }
23
+ it { should match("<input type='checkbox' checked='checked' />") }
24
+ it { should match("<input type='checkbox' checked='1' />") }
25
+ end
26
+
27
+ context "have_checkbox.not_checked" do
28
+ subject { have_checkbox.not_checked }
29
+ it { should match("<input type='checkbox' />") }
30
+ it { should_not match("<input type='checkbox' checked />") }
31
+ it { should_not match("<input type='checkbox' checked='checked' />") }
32
+ it { should_not match("<input type='checkbox' checked='1' />") }
33
+ end
34
+ end
35
+
36
+ describe "matching input names" do
37
+ context "have_checkbox.for(:user => :admin)" do
38
+ subject { have_checkbox.for(:user => :admin) }
39
+ it { should_not match("<input type='checkbox' />") }
40
+ it { should_not match("<input type='checkbox' name='user' />") }
41
+ it { should match("<input type='checkbox' name='user[admin]' />") }
42
+ it { should_not match("<input type='checkbox' name='user[admin][root]' />") }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::TagMatchers::HasForm do
4
+ include RSpec::TagMatchers
5
+
6
+ describe "matching form tags" do
7
+ context "have_form" do
8
+ subject { have_form }
9
+ it { should match("<form></form>") }
10
+ it { should match("<form method='POST'></form>") }
11
+ it { should match("<FORM></FORM>") }
12
+ it { should_not match("<foo></foo>") }
13
+ it { should_not match("<former></former>") }
14
+ it { should_not match("form") }
15
+ end
16
+ end
17
+
18
+ describe "matching form methods" do
19
+ context "have_form.with_verb(:get)" do
20
+ subject { have_form.with_verb(:get) }
21
+ it { should_not match("<form></form>") }
22
+ it { should match("<form method='GET'></form>") }
23
+ it { should match("<form method='get'></form>") }
24
+ it { should_not match("<form method='POST'></form>") }
25
+ end
26
+
27
+ context "have_form.with_verb(:post)" do
28
+ subject { have_form.with_verb(:post) }
29
+ it { should_not match("<form></form>") }
30
+ it { should_not match("<form method='GET'></form>") }
31
+ it { should match("<form method='POST'></form>") }
32
+ it { should match("<form method='post'></form>") }
33
+ it { should_not match("<form method='post'><input type='hidden' name='_method' value='put' /></form>") }
34
+ it { should_not match("<form method='post'><input type='hidden' name='_method' value='PUT' /></form>") }
35
+ end
36
+
37
+ context "have_form.with_verb(:put)" do
38
+ subject { have_form.with_verb(:put) }
39
+ it { should_not match("<form></form>") }
40
+ it { should_not match("<form method='POST'></form>") }
41
+ it { should_not match("<form method='post'></form>") }
42
+ it { should match("<form method='post'><input type='hidden' name='_method' value='put' /></form>") }
43
+ it { should match("<form method='post'><input type='hidden' name='_method' value='PUT' /></form>") }
44
+ end
45
+ end
46
+
47
+ describe "matching form actions" do
48
+ context "have_form.with_action('/foobar')" do
49
+ subject { have_form.with_action("/foobar") }
50
+ it { should_not match("<form></form>") }
51
+ it { should match("<form action='/foobar'></form>") }
52
+ it { should_not match("<form action='/foobarz'></form>") }
53
+ it { should_not match("<form action='/baz'></form>") }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::TagMatchers::HasInput do
4
+ include RSpec::TagMatchers
5
+
6
+ describe "matching input tags" do
7
+ context "have_input" do
8
+ subject { have_input }
9
+ it { should match("<input />") }
10
+ it { should match("<input type='text' />") }
11
+ it { should match("<INPUT />") }
12
+ it { should_not match("<inputer />") }
13
+ it { should_not match("input") }
14
+ end
15
+ end
16
+
17
+ describe "matching input names" do
18
+ context "have_input.for(:user => :name)" do
19
+ subject { have_input.for(:user => :name) }
20
+ it { should_not match("<input />") }
21
+ it { should match("<input name='user[name]' />") }
22
+ it { should_not match("<input name='user[name][first]' />") }
23
+ end
24
+
25
+ context "have_input.for(:user, :name)" do
26
+ subject { have_input.for(:user, :name) }
27
+ it { should_not match("<input />") }
28
+ it { should match("<input name='user[name]' />") }
29
+ it { should_not match("<input name='user[name][first]' />") }
30
+ end
31
+
32
+ context "have_input.for(:user => {:name => :first})" do
33
+ subject { have_input.for(:user => {:name => :first}) }
34
+ it { should_not match("<input />") }
35
+ it { should_not match("<input name='user[name]' />") }
36
+ it { should match("<input name='user[name][first]' />") }
37
+ end
38
+
39
+ context "have_input.for(:user, :name => :first)" do
40
+ subject { have_input.for(:user, :name => :first) }
41
+ it { should_not match("<input />") }
42
+ it { should_not match("<input name='user[name]' />") }
43
+ it { should match("<input name='user[name][first]' />") }
44
+ end
45
+
46
+ context "have_input.for(:user, :name, :first)" do
47
+ subject { have_input.for(:user, :name, :first) }
48
+ it { should_not match("<input />") }
49
+ it { should_not match("<input name='user[name]' />") }
50
+ it { should match("<input name='user[name][first]' />") }
51
+ end
52
+ end
53
+ end