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.
- data/.document +7 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +49 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +45 -0
- data/Rakefile +37 -0
- data/VERSION +1 -0
- data/core_ext/deep_flattening.rb +37 -0
- data/lib/rspec/tag_matchers/has_checkbox.rb +60 -0
- data/lib/rspec/tag_matchers/has_form.rb +84 -0
- data/lib/rspec/tag_matchers/has_input.rb +111 -0
- data/lib/rspec/tag_matchers/has_select.rb +20 -0
- data/lib/rspec/tag_matchers/has_tag.rb +221 -0
- data/lib/rspec/tag_matchers/has_time_select.rb +22 -0
- data/lib/rspec/tag_matchers/multiple_input_matcher.rb +114 -0
- data/lib/rspec/tag_matchers.rb +13 -0
- data/lib/rspec-tag_matchers.rb +1 -0
- data/spec/core_ext/deep_flattening_spec.rb +133 -0
- data/spec/lib/rspec/tag_matchers/has_checkbox_spec.rb +45 -0
- data/spec/lib/rspec/tag_matchers/has_form_spec.rb +56 -0
- data/spec/lib/rspec/tag_matchers/has_input_spec.rb +53 -0
- data/spec/lib/rspec/tag_matchers/has_select_spec.rb +26 -0
- data/spec/lib/rspec/tag_matchers/has_tag_spec.rb +294 -0
- data/spec/lib/rspec/tag_matchers/has_time_select_spec.rb +59 -0
- data/spec/lib/rspec/tag_matchers/multiple_input_matcher_spec.rb +139 -0
- data/spec/spec_helper.rb +18 -0
- metadata +168 -0
@@ -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
|