rspec-tag_matchers 0.1.0 → 0.1.1

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/README.rdoc CHANGED
@@ -22,7 +22,7 @@ containing HTML (this includes Nokogiri classes). They assume Rails conventions.
22
22
  <tt>rspec-tag_matchers</tt> is tested against Ruby 1.8.7, 1.9.2, 1.9.3, REE,
23
23
  Rubinius (1.8 mode) and JRuby.
24
24
 
25
- {<img src="http://travis-ci.org/dcuddeback/rspec-tag_matchers.png?branch=master" />}[http://travis-ci.org/dcuddeback/rspec-tag_matchers]
25
+ {<img src="https://secure.travis-ci.org/dcuddeback/rspec-tag_matchers.png?branch=master" />}[http://travis-ci.org/dcuddeback/rspec-tag_matchers]
26
26
 
27
27
  The library is well tested, but the collection of matchers is incomplete. I'm adding more on an
28
28
  as-needed basis. Please feel free to fork this repository to add your own then send me a pull
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -0,0 +1,140 @@
1
+ require 'rspec/tag_matchers/multiple_input_matcher'
2
+
3
+ module RSpec::TagMatchers
4
+ # Matches inputs generated by Rails' +date_select+ helper.
5
+ #
6
+ # @modifier discard
7
+ # Specifies that one or more components of the date are discarded in the +date_select+ helper.
8
+ # For example, <tt>discard(:day)</tt> should match the output of a +date_select+ helper that was
9
+ # given the option <tt>:discard_day => true</tt>. Note that this changes the matching criteria
10
+ # to expect a hidden input instead of a +<select>+ element for the components specified.
11
+ #
12
+ # @example Matching a date select for an event's +start_date+
13
+ # it { should have_date_select.for(:event => :start_date) }
14
+ #
15
+ # @example Matching a date select for a credit card expiration
16
+ # it { should have_date_select.discard(:day).for(:credit_card => :expiration) }
17
+ #
18
+ # @see HasDateSelect#discard
19
+ #
20
+ # @return [HasDateSelect]
21
+ def have_date_select
22
+ HasDateSelect.new
23
+ end
24
+
25
+ # A matcher that matches Rails' +date_select+ drop-downs.
26
+ class HasDateSelect < MultipleInputMatcher
27
+ include Helpers::SentenceHelper
28
+
29
+ # Initializes a HasDateSelect matcher.
30
+ def initialize
31
+ super('1i' => HasSelect.new, '2i' => HasSelect.new, '3i' => HasSelect.new)
32
+ end
33
+
34
+ # Specifies that one or more components of the date will be hidden by Rails' +date_select+
35
+ # helper. For example, calling +date_select+ with the option <tt>:discard_day => true</tt>
36
+ # produces two +<select>+ elments (for the year and month components) and one hidden input for
37
+ # the date component:
38
+ #
39
+ # <select name="model[date(1i)]">...</select>
40
+ # <select name="model[date(2i)]">...</select>
41
+ # <input type="hidden" name="model[date(3i)]" />
42
+ #
43
+ # Since the default is to match three +<select>+ elements, one must call +discard(:day)+ to tell
44
+ # a HasDateSelect matcher to expect a hidden input for the third component.
45
+ #
46
+ # @note This modifier replaces the matchers for the given date components. Any modifiers that
47
+ # were called before this one will be lost for those matchers. Therefor, +discard+ should be
48
+ # called before other modifiers, such as +for+. For example, the following matcher
49
+ # will only test the input names for the month and day components.
50
+ #
51
+ # it { should have_date_select.for(:start_date).discard(:year) }
52
+ #
53
+ # In order to test the input names for all three components, the +for+ modifier must come
54
+ # after the +discard+ modifier:
55
+ #
56
+ # it { should have_date_select.discard(:year).for(:start_date) }
57
+ #
58
+ # @param [Symbols] *parts A variable number of symbols specifying which components should be
59
+ # discarded. Valid symbols are +:year+, +:month+, and +:day+.
60
+ #
61
+ # @return [HasDateSelect] self
62
+ def discard(*parts)
63
+ @discard ||= []
64
+ @discard += parts
65
+
66
+ parts.each do |part|
67
+ # TODO: create a HasHiddenInput matcher
68
+ replace_matcher(part, HasInput.new.with_attribute(:type => :hidden))
69
+ end
70
+ self
71
+ end
72
+
73
+ # Returns a description of the matcher's criteria.
74
+ #
75
+ # @return [String]
76
+ def description
77
+ [basic_description, extra_description].compact.reject(&:empty?).join(" ")
78
+ end
79
+
80
+ # Returns an explanation of why the matcher failed to match with +should+.
81
+ #
82
+ # @return [String]
83
+ def failure_message
84
+ "expected document to #{description}; got: #{@rendered}"
85
+ end
86
+
87
+ # Returns an explanation of why the matcher failed to match with +should_not+.
88
+ #
89
+ # @return [String]
90
+ def negative_failure_message
91
+ "expected document to not #{description}; got: #{@rendered}"
92
+ end
93
+
94
+ private
95
+
96
+ # Replaces the matcher for the given date component.
97
+ #
98
+ # @param [Symbol] part The date component whose matcher should be replaced.
99
+ # @param [HasInput] new_matcher The matcher to replace the old one.
100
+ def replace_matcher(part, new_matcher)
101
+ key = key_for(part)
102
+ @components[key] = new_matcher.with_attribute(:name => /\(#{key}\)/)
103
+ end
104
+
105
+ # Looks up the key for the given date component.
106
+ #
107
+ # @param [Symbol] part A date component (+:year+, +:month+, or +:day+).
108
+ #
109
+ # @return [String] The requested key
110
+ def key_for(part)
111
+ {
112
+ :year => '1i',
113
+ :month => '2i',
114
+ :day => '3i'
115
+ }[part]
116
+ end
117
+
118
+ # Returns a basic description.
119
+ #
120
+ # @return [String]
121
+ def basic_description
122
+ "have date select"
123
+ end
124
+
125
+ # Provides an extra description fragment that can be appended to the basic description.
126
+ #
127
+ # @return [String]
128
+ def extra_description
129
+ [for_description, discard_description].compact.join(" ")
130
+ end
131
+
132
+ def for_description
133
+ "for #{@for.join(".")}" if @for
134
+ end
135
+
136
+ def discard_description
137
+ "without #{make_sentence(@discard, :conjunction => "or")}" if @discard
138
+ end
139
+ end
140
+ end
@@ -6,10 +6,16 @@ module RSpec::TagMatchers
6
6
  # @modifier for
7
7
  # Adds a criteria that the input must be for the given attribute.
8
8
  #
9
+ # @modifier value
10
+ # Adds a criteria that the input must have a given value.
11
+ #
9
12
  # @example Matching an input for the +name+ attribute on +user+
10
13
  # it { should have_input.for(:user => :name) }
11
14
  # it { should have_input.for(:user, :name) }
12
15
  #
16
+ # @example Matching an input with a value of <tt>"42"</tt>.
17
+ # it { should have_input.value("42") }
18
+ #
13
19
  # @return [HasInput]
14
20
  #
15
21
  # @see HasInput#for
@@ -88,6 +94,15 @@ module RSpec::TagMatchers
88
94
  self
89
95
  end
90
96
 
97
+ # Adds a criteria that the input's value must match a certain value.
98
+ #
99
+ # @param [String, Symbol, Regexp, true, false] value
100
+ #
101
+ # @return [HasInput] self
102
+ def value(value)
103
+ with_attribute(:value => value)
104
+ end
105
+
91
106
  private
92
107
 
93
108
  # Converts an array or hash of names to a name for an input form.
@@ -90,6 +90,7 @@ module RSpec::TagMatchers
90
90
  # provide additional methods that can be chained from the matcher to provide tag-specific
91
91
  # criteria. See {HasTag#with_criteria} for how to add custom criteria to a matcher.
92
92
  class HasTag
93
+ include Helpers::SentenceHelper
93
94
 
94
95
  # Constructs a matcher that matches HTML tags by +name+.
95
96
  #
@@ -100,6 +101,27 @@ module RSpec::TagMatchers
100
101
  @criteria = []
101
102
  end
102
103
 
104
+ # Returns a description of the matcher's criteria. The description is used in RSpec's output.
105
+ #
106
+ # @return [String]
107
+ def description
108
+ "have #{@name.inspect} tag #{extra_description}".strip
109
+ end
110
+
111
+ # Returns an explanation of why the matcher failed to match with +should+.
112
+ #
113
+ # @return [String]
114
+ def failure_message
115
+ "expected document to #{description}; got: #{@rendered}"
116
+ end
117
+
118
+ # Returns an explanation of why the matcher failed to match with +should_not+.
119
+ #
120
+ # @return [String]
121
+ def negative_failure_message
122
+ "expected document to not #{description}; got: #{@rendered}"
123
+ end
124
+
103
125
  # Answers whether or not the matcher matches any elements within +rendered+.
104
126
  #
105
127
  # @param [String] rendered A string of HTML or an Object whose +to_s+ method returns HTML.
@@ -107,7 +129,8 @@ module RSpec::TagMatchers
107
129
  #
108
130
  # @return [Boolean]
109
131
  def matches?(rendered)
110
- Nokogiri::HTML::Document.parse(rendered.to_s).css(@name).select do |element|
132
+ @rendered = rendered
133
+ Nokogiri::HTML::Document.parse(@rendered.to_s).css(@name).select do |element|
111
134
  matches_attributes?(element) && matches_criteria?(element)
112
135
  end.length > 0
113
136
  end
@@ -189,7 +212,6 @@ module RSpec::TagMatchers
189
212
 
190
213
  private
191
214
 
192
-
193
215
  # Answers whether or not +element+ matches the attributes in the attributes hash given to
194
216
  # {#with_attribute}.
195
217
  #
@@ -217,5 +239,85 @@ module RSpec::TagMatchers
217
239
  end
218
240
  end
219
241
  end
242
+
243
+ # Provides extra description that can be appended to the basic description.
244
+ #
245
+ # @return [String]
246
+ def extra_description
247
+ attributes_description
248
+ end
249
+
250
+ # Returns a description of the attribute criteria. For example, the description of an attribute
251
+ # criteria of <tt>with_attribute(:foo => "bar")</tt> will look like <tt>'with attribute
252
+ # foo="bar"'</tt>.
253
+ #
254
+ # @return [String]
255
+ def attributes_description
256
+ grouped_attributes = @attributes.group_by { |key, value| !!value }
257
+
258
+ make_sentence(
259
+ # Yes, this is functionally equivalent to grouped_attributes.map, except this forces the
260
+ # keys to be evalutated in the order [true, false]. This is necessary to maintain
261
+ # compatibility with Ruby 1.8.7, because hashes in 1.8.7 aren't ordered.
262
+ [true, false].reduce([]) do |memo, group_key|
263
+ attributes = grouped_attributes[group_key]
264
+
265
+ if attributes
266
+ memo << grouped_attributes_prefix(group_key, attributes.count > 1) +
267
+ grouped_attributes_description(attributes)
268
+ end
269
+
270
+ memo
271
+ end
272
+ )
273
+ end
274
+
275
+ # Provides a prefix that can be used before a list of attribute criteria. Possible oututs are
276
+ # <tt>"with attribute"</tt>, <tt>"with attributes"</tt>, <tt>"without attribute"</tt>, and
277
+ # <tt>"without attributes"</tt>.
278
+ #
279
+ # @param [Boolean] value Whether this should prefix inclusive attributes. Selects between
280
+ # <tt>"with"</tt> and <tt>"without"</tt>.
281
+ # @param [Boolean] plural Whether this prefixes multiple attributes. Selects between
282
+ # <tt>"attribute"</tt> and <tt>"attributes"</tt>.
283
+ #
284
+ # @return [String]
285
+ def grouped_attributes_prefix(value, plural)
286
+ (value ? "with " : "without ") + (plural ? "attributes " : "attribute ")
287
+ end
288
+
289
+ # Describes a group of attribute criteria, combining them into a sentence fragment, with
290
+ # punctuation and conjunctions if necessary.
291
+ #
292
+ # @param [Array] attributes A list of <tt>[key, value]</tt> pairs that describe the attribute
293
+ # criteria.
294
+ #
295
+ # @return [String]
296
+ def grouped_attributes_description(attributes)
297
+ make_sentence(
298
+ attributes.sort_by{ |key, value| key.to_s }.map do |key, value|
299
+ attribute_description(key, value)
300
+ end
301
+ )
302
+ end
303
+
304
+ # Returns a string describing the criteria for matching attribute +key+ with +value+.
305
+ #
306
+ # @param [Symbol] key The attribute's key.
307
+ # @param [Object] value The attribute's expected value.
308
+ #
309
+ # @return [String]
310
+ def attribute_description(key, value)
311
+ case value
312
+ when true
313
+ "#{key}=anything"
314
+ when false
315
+ key.to_s
316
+ when Regexp
317
+ "#{key}=~#{value.inspect}"
318
+ else
319
+ "#{key}=#{value.inspect}"
320
+ end
321
+ end
220
322
  end
221
323
  end
@@ -16,7 +16,45 @@ module RSpec::TagMatchers
16
16
 
17
17
  # Initializes a HasTimeSelect matcher.
18
18
  def initialize
19
- super(4 => HasSelect.new, 5 => HasSelect.new)
19
+ super('4i' => HasSelect.new, '5i' => HasSelect.new)
20
20
  end
21
+
22
+ # Returns a description of the matcher's criteria.
23
+ #
24
+ # @return [String]
25
+ def description
26
+ [basic_description, extra_description].compact.join(" ")
27
+ end
28
+
29
+ # Returns an explanation of why the matcher failed to match with +should+.
30
+ #
31
+ # @return [String]
32
+ def failure_message
33
+ "expected document to #{description}; got: #{@rendered}"
34
+ end
35
+
36
+ # Returns an explanation of why the matcher failed to match with +should_not+.
37
+ #
38
+ # @return [String]
39
+ def negative_failure_message
40
+ "expected document to not #{description}; got: #{@rendered}"
41
+ end
42
+
43
+ private
44
+
45
+ # Returns a basic description.
46
+ #
47
+ # @return [String]
48
+ def basic_description
49
+ "have time select"
50
+ end
51
+
52
+ # Provides an extra description fragment that can be appended to the basic description.
53
+ #
54
+ # @return [String]
55
+ def extra_description
56
+ "for #{@for.join(".")}" if @for
57
+ end
58
+
21
59
  end
22
60
  end
@@ -0,0 +1,38 @@
1
+ module RSpec::TagMatchers::Helpers
2
+ module SentenceHelper
3
+ # Joins multiple strings into a sentence with punctuation and conjunctions.
4
+ #
5
+ # @example Forming sentences
6
+ # make_sentence("foo") # => "foo"
7
+ # make_sentence("foo", "bar") # => "foo and bar"
8
+ # make_sentence("foo", "bar", "baz") # => "foo, bar, and baz"
9
+ #
10
+ # @example Overriding the conjunction
11
+ # make_sentence("foo", "bar", "baz", :conjunction => "or") # => "foo, bar, or baz"
12
+ #
13
+ # @param [Strings] *strings A list of strings to be combined into a sentence. The last item
14
+ # can be an options hash.
15
+ #
16
+ # @option *strings.last [String] :conjunction ("and") The conjunction to use to join sentence
17
+ # fragments.
18
+ #
19
+ # @return [String]
20
+ def make_sentence(*strings)
21
+ options = strings.last.is_a?(Hash) ? strings.pop : {}
22
+ strings = strings.flatten.map(&:to_s).reject(&:empty?)
23
+ conjunction = options[:conjunction] || "and"
24
+
25
+ case strings.count
26
+ when 0
27
+ ""
28
+ when 1
29
+ strings.first
30
+ else
31
+ last = strings.pop
32
+ puncuation = strings.count > 1 ? ", " : " "
33
+
34
+ [strings, "#{conjunction} #{last}"].flatten.join(puncuation)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -6,9 +6,9 @@ module RSpec::TagMatchers
6
6
  #
7
7
  # @example Building a date matcher
8
8
  # matcher = MultipleInputMatcher.new(
9
- # 1 => HasSelect.new,
10
- # 2 => HasSelect.new,
11
- # 3 => HasSelect.new
9
+ # '1i' => HasSelect.new,
10
+ # '2i' => HasSelect.new,
11
+ # '3i' => HasSelect.new
12
12
  # )
13
13
  # matcher.for(:user => :birthday) # will match <select> tags with names
14
14
  # # "user[birthday(1i)]", "user[birthday(2i)]", and
@@ -16,21 +16,20 @@ module RSpec::TagMatchers
16
16
  #
17
17
  # @example Building a time matcher
18
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
19
+ # '4i' => HasSelect.new, # expressions: /\(4i\)/ and /\(5i\)/
20
+ # '5i' => HasSelect.new
21
21
  # )
22
- #
23
- #
24
22
  class MultipleInputMatcher
25
23
 
26
24
  # Initializes a matcher that matches multiple input elements.
27
25
  #
28
- # @param [Hash] components A hash of matchers. The keys serve as indices and the values are the
29
- # matchers that must be satisfied.
26
+ # @param [Hash] components A hash of matchers. The keys should be the keys used in Rails'
27
+ # multi-parameter assignment, e.g., <tt>"1i"</tt>, <tt>"2s"</tt>, etc,
28
+ # and the values are the matchers that must be satisfied.
30
29
  def initialize(components)
31
30
  @components = components
32
- @components.each do |index, matcher|
33
- matcher.with_attribute(:name => /\(#{index}i\)/)
31
+ @components.each do |key, matcher|
32
+ matcher.with_attribute(:name => /\(#{key}\)/)
34
33
  end
35
34
  end
36
35
 
@@ -43,6 +42,7 @@ module RSpec::TagMatchers
43
42
  #
44
43
  # @return [Boolean]
45
44
  def matches?(rendered)
45
+ @rendered = rendered
46
46
  @failures = matchers.reject do |matcher|
47
47
  matcher.matches?(rendered)
48
48
  end
@@ -50,14 +50,14 @@ module RSpec::TagMatchers
50
50
  @failures.empty?
51
51
  end
52
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
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 key to the last
55
55
  # component of the input's name.
56
56
  #
57
57
  # @example Input naming delegation
58
58
  # hour_matcher = HasSelect.new
59
59
  # minute_matcher = HasSelect.new
60
- # time_matcher = MultipleInputMatcher.new(4 => hour_matcher, 5 => minute_matcher)
60
+ # time_matcher = MultipleInputMatcher.new('4i' => hour_matcher, '5i' => minute_matcher)
61
61
  #
62
62
  # time_matcher.for(:event => :start_time) # calls hour_matcher.for("event", "start_time(4i)")
63
63
  # # and minute_matcher.for("event", "start_time(5i)")
@@ -66,8 +66,12 @@ module RSpec::TagMatchers
66
66
  #
67
67
  # @return [MultipleInputMatcher] self
68
68
  def for(*args)
69
+ @for = args.dup
70
+ @for.extend(DeepFlattening)
71
+ @for = @for.deep_flatten
72
+
69
73
  @components.each do |index, matcher|
70
- delegated_for(index, matcher, args)
74
+ delegated_for(index, matcher, @for)
71
75
  end
72
76
  self
73
77
  end
@@ -95,19 +99,16 @@ module RSpec::TagMatchers
95
99
  @components.values
96
100
  end
97
101
 
98
- # Set's +matcher+'s input name according to +args+ and +index+.
102
+ # Set's +matcher+'s input name according to +args+ and +key+.
99
103
  #
100
- # @param [Integer] index The matcher's index.
104
+ # @param [String] key The matcher's key.
101
105
  # @param [HasInput] matcher The matcher.
102
106
  # @param [Arrah, Hash] args A hierarchy of names that would normally be passed to
103
107
  # {HasInput#for}.
104
- def delegated_for(index, matcher, args)
105
- args = args.dup
106
- args.extend(DeepFlattening)
107
- args = args.deep_flatten
108
-
108
+ def delegated_for(key, matcher, args)
109
+ args = args.dup
109
110
  args[-1] = args[-1].to_s
110
- args[-1] += "(#{index}i)"
111
+ args[-1] += "(#{key})"
111
112
  matcher.for(*args)
112
113
  end
113
114
  end
@@ -2,6 +2,9 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'core_ext'))
2
2
 
3
3
  module RSpec
4
4
  module TagMatchers
5
+ module Helpers
6
+ autoload :SentenceHelper, 'rspec/tag_matchers/helpers/sentence_helper'
7
+ end
5
8
  end
6
9
  end
7
10
 
@@ -11,3 +14,4 @@ require 'rspec/tag_matchers/has_input'
11
14
  require 'rspec/tag_matchers/has_checkbox'
12
15
  require 'rspec/tag_matchers/has_select'
13
16
  require 'rspec/tag_matchers/has_time_select'
17
+ require 'rspec/tag_matchers/has_date_select'
@@ -0,0 +1,93 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rspec-tag_matchers}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{David Cuddeback}]
12
+ s.date = %q{2012-02-18}
13
+ s.description = %q{A collection of RSpec matchers that understand Rails conventions, allowing for more concise specs.}
14
+ s.email = %q{david.cuddeback@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".travis.yml",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "Guardfile",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "core_ext/deep_flattening.rb",
31
+ "lib/rspec-tag_matchers.rb",
32
+ "lib/rspec/tag_matchers.rb",
33
+ "lib/rspec/tag_matchers/has_checkbox.rb",
34
+ "lib/rspec/tag_matchers/has_date_select.rb",
35
+ "lib/rspec/tag_matchers/has_form.rb",
36
+ "lib/rspec/tag_matchers/has_input.rb",
37
+ "lib/rspec/tag_matchers/has_select.rb",
38
+ "lib/rspec/tag_matchers/has_tag.rb",
39
+ "lib/rspec/tag_matchers/has_time_select.rb",
40
+ "lib/rspec/tag_matchers/helpers/sentence_helper.rb",
41
+ "lib/rspec/tag_matchers/multiple_input_matcher.rb",
42
+ "rspec-tag_matchers.gemspec",
43
+ "spec/core_ext/deep_flattening_spec.rb",
44
+ "spec/lib/rspec/tag_matchers/has_checkbox_spec.rb",
45
+ "spec/lib/rspec/tag_matchers/has_date_select_spec.rb",
46
+ "spec/lib/rspec/tag_matchers/has_form_spec.rb",
47
+ "spec/lib/rspec/tag_matchers/has_input_spec.rb",
48
+ "spec/lib/rspec/tag_matchers/has_select_spec.rb",
49
+ "spec/lib/rspec/tag_matchers/has_tag_spec.rb",
50
+ "spec/lib/rspec/tag_matchers/has_time_select_spec.rb",
51
+ "spec/lib/rspec/tag_matchers/multiple_input_matcher_spec.rb",
52
+ "spec/spec_helper.rb"
53
+ ]
54
+ s.homepage = %q{http://github.com/dcuddeback/rspec-tag_matchers}
55
+ s.licenses = [%q{MIT}]
56
+ s.require_paths = [%q{lib}]
57
+ s.rubygems_version = %q{1.8.6}
58
+ s.summary = %q{RSpec matchers for Rails views}
59
+
60
+ if s.respond_to? :specification_version then
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
65
+ s.add_development_dependency(%q<rspec>, [">= 0"])
66
+ s.add_development_dependency(%q<yard>, [">= 0"])
67
+ s.add_development_dependency(%q<bundler>, [">= 0"])
68
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
69
+ s.add_development_dependency(%q<guard>, [">= 0"])
70
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
71
+ s.add_development_dependency(%q<guard-bundler>, [">= 0"])
72
+ else
73
+ s.add_dependency(%q<nokogiri>, [">= 0"])
74
+ s.add_dependency(%q<rspec>, [">= 0"])
75
+ s.add_dependency(%q<yard>, [">= 0"])
76
+ s.add_dependency(%q<bundler>, [">= 0"])
77
+ s.add_dependency(%q<jeweler>, [">= 0"])
78
+ s.add_dependency(%q<guard>, [">= 0"])
79
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
80
+ s.add_dependency(%q<guard-bundler>, [">= 0"])
81
+ end
82
+ else
83
+ s.add_dependency(%q<nokogiri>, [">= 0"])
84
+ s.add_dependency(%q<rspec>, [">= 0"])
85
+ s.add_dependency(%q<yard>, [">= 0"])
86
+ s.add_dependency(%q<bundler>, [">= 0"])
87
+ s.add_dependency(%q<jeweler>, [">= 0"])
88
+ s.add_dependency(%q<guard>, [">= 0"])
89
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
90
+ s.add_dependency(%q<guard-bundler>, [">= 0"])
91
+ end
92
+ end
93
+
@@ -0,0 +1,196 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::TagMatchers::HasDateSelect do
4
+ include RSpec::TagMatchers
5
+
6
+ # single inputs for start_date
7
+ let(:start_date_year) { "<select name='event[start_date(1i)]'></select>" }
8
+ let(:start_date_month) { "<select name='event[start_date(2i)]'></select>" }
9
+ let(:start_date_day) { "<select name='event[start_date(3i)]'></select>" }
10
+
11
+ # single inputs for end_date
12
+ let(:end_date_year) { "<select name='event[end_date(1i)]'></select>" }
13
+ let(:end_date_month) { "<select name='event[end_date(2i)]'></select>" }
14
+ let(:end_date_day) { "<select name='event[end_date(3i)]'></select>" }
15
+
16
+ # hidden inputs
17
+ let(:hidden_start_date_year) { "<input type='hidden' name='event[start_date(1i)]' />" }
18
+ let(:hidden_start_date_month) { "<input type='hidden' name='event[start_date(2i)]' />" }
19
+ let(:hidden_start_date_day) { "<input type='hidden' name='event[start_date(3i)]' />" }
20
+ let(:hidden_other) { "<input type='hidden' name='foo' />" }
21
+
22
+ # combined inputs for start_date
23
+ let(:start_date_with_year_month_and_day) { start_date_year + start_date_month + start_date_day }
24
+ let(:start_date_with_year_and_month) { start_date_year + start_date_month }
25
+
26
+ # combined inputs for end_date
27
+ let(:end_date_with_year_and_month) { end_date_year + end_date_month }
28
+ let(:end_date_with_year_month_and_day) { end_date_year + end_date_month + end_date_day }
29
+
30
+ # combined inputs for start_date with hidden inputs
31
+ let(:start_date_with_year_and_month_and_hidden_other) { start_date_year + start_date_month + hidden_other }
32
+ let(:start_date_with_year_and_month_and_hidden_day) { start_date_year + start_date_month + hidden_start_date_day }
33
+ let(:start_date_with_year_and_hidden_month_and_day) { start_date_year + hidden_start_date_month + start_date_day }
34
+ let(:start_date_with_hidden_year_and_month_and_day) { hidden_start_date_year + start_date_month + start_date_day }
35
+ let(:start_date_with_year_and_hidden_month_and_hidden_day) { start_date_year + hidden_start_date_month + hidden_start_date_day }
36
+
37
+ describe "date select matching" do
38
+ context "have_date_select" do
39
+ subject { have_date_select }
40
+
41
+ it { should match(start_date_with_year_month_and_day) }
42
+ it { should_not match(start_date_with_year_and_month) }
43
+ it { should_not match(start_date_year) }
44
+ it { should_not match(start_date_month) }
45
+ it { should_not match(start_date_day) }
46
+
47
+ it { should match(end_date_with_year_month_and_day) }
48
+ it { should_not match(end_date_with_year_and_month) }
49
+ it { should_not match(end_date_year) }
50
+ it { should_not match(end_date_month) }
51
+ it { should_not match(end_date_day) }
52
+ end
53
+ end
54
+
55
+ describe "matching discarded date parts" do
56
+ context "have_date_select.discard(:day)" do
57
+ subject { have_date_select.discard(:day) }
58
+
59
+ it { should match(start_date_with_year_and_month_and_hidden_day) }
60
+ it { should_not match(start_date_with_year_and_month_and_hidden_other) }
61
+ it { should_not match(start_date_with_year_month_and_day) }
62
+ it { should_not match(start_date_with_year_and_month) }
63
+ end
64
+
65
+ context "have_date_select.discard(:month)" do
66
+ subject { have_date_select.discard(:month) }
67
+
68
+ it { should match(start_date_with_year_and_hidden_month_and_day) }
69
+ it { should_not match(start_date_with_year_month_and_day) }
70
+ end
71
+
72
+ context "have_date_select.discard(:year)" do
73
+ subject { have_date_select.discard(:year) }
74
+
75
+ it { should match(start_date_with_hidden_year_and_month_and_day) }
76
+ it { should_not match(start_date_with_year_month_and_day) }
77
+ end
78
+
79
+ context "have_date_select.discard(:month, :day)" do
80
+ subject { have_date_select.discard(:month, :day) }
81
+
82
+ it { should match(start_date_with_year_and_hidden_month_and_hidden_day) }
83
+ it { should_not match(start_date_with_year_month_and_day) }
84
+ end
85
+ end
86
+
87
+ describe "matching input names" do
88
+ context "have_date_select.for(:event => :start_date)" do
89
+ subject { have_date_select.for(:event => :start_date) }
90
+
91
+ it { should match(start_date_with_year_month_and_day) }
92
+ it { should_not match(start_date_with_year_and_month) }
93
+
94
+ it { should_not match(end_date_with_year_and_month) }
95
+ it { should_not match(end_date_with_year_month_and_day) }
96
+ end
97
+
98
+ context "have_date_select.for(:event => :end_date)" do
99
+ subject { have_date_select.for(:event => :end_date) }
100
+
101
+ it { should match(end_date_with_year_month_and_day) }
102
+ it { should_not match(end_date_with_year_and_month) }
103
+
104
+ it { should_not match(start_date_with_year_and_month) }
105
+ it { should_not match(start_date_with_year_month_and_day) }
106
+ end
107
+ end
108
+
109
+ describe "#description" do
110
+ context "for simple date select" do
111
+ context "have_date_select" do
112
+ subject { have_date_select.description }
113
+ it { should == "have date select" }
114
+ end
115
+ end
116
+
117
+ context "for date select with attribute matcher" do
118
+ context "have_date_select.for(:start_date)" do
119
+ subject { have_date_select.for(:start_date).description }
120
+ it { should == "have date select for start_date" }
121
+ end
122
+
123
+ context "have_date_select.for(:event => :start_date)" do
124
+ subject { have_date_select.for(:event => :start_date).description }
125
+ it { should == "have date select for event.start_date" }
126
+ end
127
+
128
+ context "have_date_select.for(:event, :start => :date)" do
129
+ subject { have_date_select.for(:event, :start => :date).description }
130
+ it { should == "have date select for event.start.date" }
131
+ end
132
+ end
133
+
134
+ context "for date select with discarded components" do
135
+ context "have_date_select.discard(:day)" do
136
+ subject { have_date_select.discard(:day).description }
137
+ it { should == "have date select without day" }
138
+ end
139
+
140
+ context "have_date_select.discard(:month)" do
141
+ subject { have_date_select.discard(:month).description }
142
+ it { should == "have date select without month" }
143
+ end
144
+
145
+ context "have_date_select.discard(:year)" do
146
+ subject { have_date_select.discard(:year).description }
147
+ it { should == "have date select without year" }
148
+ end
149
+
150
+ context "have_date_select.discard(:day, :month)" do
151
+ subject { have_date_select.discard(:day, :month).description }
152
+ it { should == "have date select without day or month" }
153
+ end
154
+
155
+ context "have_date_select.discard(:day, :month, :year)" do
156
+ subject { have_date_select.discard(:day, :month, :year).description }
157
+ it { should == "have date select without day, month, or year" }
158
+ end
159
+ end
160
+
161
+ context "for date select with attribute matcher and discarded components" do
162
+ context "have_date_select.discard(:day).for(:event => :start_date)" do
163
+ subject { have_date_select.discard(:day).for(:event => :start_date).description }
164
+ it { should == "have date select for event.start_date without day" }
165
+ end
166
+ end
167
+ end
168
+
169
+ describe "#failure_message" do
170
+ context "for have_date_select" do
171
+ let(:matcher) { have_date_select }
172
+
173
+ context "matching '<bar></bar>'" do
174
+ let(:document) { "<bar></bar>" }
175
+
176
+ before { matcher.matches?(document) }
177
+ subject { matcher.failure_message }
178
+ it { should == "expected document to #{matcher.description}; got: #{document}" }
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "#negative_failure_message" do
184
+ context "for have_date_select" do
185
+ let(:matcher) { have_date_select }
186
+
187
+ context "matching \"<select name='(4i)'><select><select name='(5i)'></select>\"" do
188
+ let(:document) { "<select name='(4i)'><select><select name='(5i)'></select>" }
189
+
190
+ before { matcher.matches?(document) }
191
+ subject { matcher.negative_failure_message }
192
+ it { should == "expected document to not #{matcher.description}; got: #{document}" }
193
+ end
194
+ end
195
+ end
196
+ end
@@ -50,4 +50,13 @@ describe RSpec::TagMatchers::HasInput do
50
50
  it { should match("<input name='user[name][first]' />") }
51
51
  end
52
52
  end
53
+
54
+ describe "matching input values" do
55
+ context "have_input.value(:foo)" do
56
+ subject { have_input.value(:foo) }
57
+ it { should match("<input value='foo' />") }
58
+ it { should_not match("<input value='bar' />") }
59
+ it { should_not match("<input />") }
60
+ end
61
+ end
53
62
  end
@@ -291,4 +291,88 @@ describe RSpec::TagMatchers::HasTag do
291
291
  end
292
292
  end
293
293
  end
294
+
295
+ describe "#description" do
296
+ context "for simple matchers" do
297
+ context "have_tag(:foo)" do
298
+ subject { have_tag(:foo) }
299
+ its(:description) { should == 'have "foo" tag' }
300
+ end
301
+
302
+ context "have_tag(:bar)" do
303
+ subject { have_tag(:bar) }
304
+ its(:description) { should == 'have "bar" tag' }
305
+ end
306
+ end
307
+
308
+ context "for matchers with attribute criteria" do
309
+ context "have_tag(:foo).with_attribute(:bar => :baz)" do
310
+ subject { have_tag(:foo).with_attribute(:bar => :baz) }
311
+ its(:description) { should == 'have "foo" tag with attribute bar=:baz' }
312
+ end
313
+
314
+ context "have_tag(:foo).with_attribute(:bar => 'baz')" do
315
+ subject { have_tag(:foo).with_attribute(:bar => 'baz') }
316
+ its(:description) { should == 'have "foo" tag with attribute bar="baz"' }
317
+ end
318
+
319
+ context "have_tag(:foo).with_attribute(:bar => /baz/)" do
320
+ subject { have_tag(:foo).with_attribute(:bar => /baz/) }
321
+ its(:description) { should == 'have "foo" tag with attribute bar=~/baz/' }
322
+ end
323
+
324
+ context "have_tag(:foo).with_attribute(:bar => true)" do
325
+ subject { have_tag(:foo).with_attribute(:bar => true) }
326
+ its(:description) { should == 'have "foo" tag with attribute bar=anything' }
327
+ end
328
+
329
+ context "have_tag(:foo).with_attribute(:bar => false)" do
330
+ subject { have_tag(:foo).with_attribute(:bar => false) }
331
+ its(:description) { should == 'have "foo" tag without attribute bar' }
332
+ end
333
+
334
+ context "have_tag(:foo).with_attributes(:bar => '1', :baz => '2')" do
335
+ subject { have_tag(:foo).with_attributes(:bar => '1', :baz => '2') }
336
+ its(:description) { should == 'have "foo" tag with attributes bar="1" and baz="2"' }
337
+ end
338
+
339
+ context "have_tag(:foo).with_attributes(:bar => '1', :baz => '2', :qux => '3')" do
340
+ subject { have_tag(:foo).with_attributes(:bar => '1', :baz => '2', :qux => '3') }
341
+ its(:description) { should == 'have "foo" tag with attributes bar="1", baz="2", and qux="3"' }
342
+ end
343
+
344
+ context "have_tag(:foo).with_attributes(:bar => true, :baz => false)" do
345
+ subject { have_tag(:foo).with_attributes(:bar => true, :baz => false) }
346
+ its(:description) { should == 'have "foo" tag with attribute bar=anything and without attribute baz' }
347
+ end
348
+ end
349
+ end
350
+
351
+ describe "#failure_message" do
352
+ context "have_tag(:foo)" do
353
+ let(:matcher) { have_tag(:foo) }
354
+
355
+ context "matching '<bar></bar>'" do
356
+ let(:document) { "<bar></bar>" }
357
+
358
+ before { matcher.matches?(document) }
359
+ subject { matcher.failure_message }
360
+ it { should == "expected document to #{matcher.description}; got: #{document}" }
361
+ end
362
+ end
363
+ end
364
+
365
+ describe "#negative_failure_message" do
366
+ context "have_tag(:foo)" do
367
+ let(:matcher) { have_tag(:foo) }
368
+
369
+ context "matching '<foo></foo>'" do
370
+ let(:document) { "<foo></foo>" }
371
+
372
+ before { matcher.matches?(document) }
373
+ subject { matcher.negative_failure_message }
374
+ it { should == "expected document to not #{matcher.description}; got: #{document}" }
375
+ end
376
+ end
377
+ end
294
378
  end
@@ -56,4 +56,54 @@ describe RSpec::TagMatchers::HasTimeSelect do
56
56
  it { should_not match(start_time_with_hour_minute_and_second) }
57
57
  end
58
58
  end
59
+
60
+ describe "#description" do
61
+ context "for have_time_select" do
62
+ subject { have_time_select.description }
63
+ it { should == "have time select" }
64
+ end
65
+
66
+ context "for have_time_select.for(:start_time)" do
67
+ subject { have_time_select.for(:start_time).description }
68
+ it { should == "have time select for start_time" }
69
+ end
70
+
71
+ context "for have_time_select.for(:event => :start_time)" do
72
+ subject { have_time_select.for(:event => :start_time).description }
73
+ it { should == "have time select for event.start_time" }
74
+ end
75
+
76
+ context "for have_time_select.for(:event, :start => :time)" do
77
+ subject { have_time_select.for(:event, :start => :time).description }
78
+ it { should == "have time select for event.start.time" }
79
+ end
80
+ end
81
+
82
+ describe "#failure_message" do
83
+ context "for have_time_select" do
84
+ let(:matcher) { have_time_select }
85
+
86
+ context "matching '<bar></bar>'" do
87
+ let(:document) { "<bar></bar>" }
88
+
89
+ before { matcher.matches?(document) }
90
+ subject { matcher.failure_message }
91
+ it { should == "expected document to #{matcher.description}; got: #{document}" }
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#negative_failure_message" do
97
+ context "for have_time_select" do
98
+ let(:matcher) { have_time_select }
99
+
100
+ context "matching \"<select name='(4i)'><select><select name='(5i)'></select>\"" do
101
+ let(:document) { "<select name='(4i)'><select><select name='(5i)'></select>" }
102
+
103
+ before { matcher.matches?(document) }
104
+ subject { matcher.negative_failure_message }
105
+ it { should == "expected document to not #{matcher.description}; got: #{document}" }
106
+ end
107
+ end
108
+ end
59
109
  end
@@ -6,7 +6,7 @@ describe RSpec::TagMatchers::MultipleInputMatcher do
6
6
  let(:html) { "<input name='foo' /><input name='bar' />" }
7
7
 
8
8
  let(:multiple_matcher) do
9
- RSpec::TagMatchers::MultipleInputMatcher.new(2 => foo_matcher, 3 => bar_matcher)
9
+ RSpec::TagMatchers::MultipleInputMatcher.new('2i' => foo_matcher, '3i' => bar_matcher)
10
10
  end
11
11
 
12
12
  before do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-tag_matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-16 00:00:00.000000000Z
12
+ date: 2012-02-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
16
- requirement: &2161366360 !ruby/object:Gem::Requirement
16
+ requirement: &2154865400 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2161366360
24
+ version_requirements: *2154865400
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2161365880 !ruby/object:Gem::Requirement
27
+ requirement: &2154864620 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2161365880
35
+ version_requirements: *2154864620
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: yard
38
- requirement: &2161365260 !ruby/object:Gem::Requirement
38
+ requirement: &2154864000 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2161365260
46
+ version_requirements: *2154864000
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
- requirement: &2161364780 !ruby/object:Gem::Requirement
49
+ requirement: &2154863260 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2161364780
57
+ version_requirements: *2154863260
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jeweler
60
- requirement: &2161364300 !ruby/object:Gem::Requirement
60
+ requirement: &2154862600 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2161364300
68
+ version_requirements: *2154862600
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard
71
- requirement: &2161363660 !ruby/object:Gem::Requirement
71
+ requirement: &2154861880 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2161363660
79
+ version_requirements: *2154861880
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: guard-rspec
82
- requirement: &2161363180 !ruby/object:Gem::Requirement
82
+ requirement: &2154861180 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2161363180
90
+ version_requirements: *2154861180
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: guard-bundler
93
- requirement: &2161362700 !ruby/object:Gem::Requirement
93
+ requirement: &2154859720 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *2161362700
101
+ version_requirements: *2154859720
102
102
  description: A collection of RSpec matchers that understand Rails conventions, allowing
103
103
  for more concise specs.
104
104
  email: david.cuddeback@gmail.com
@@ -122,14 +122,18 @@ files:
122
122
  - lib/rspec-tag_matchers.rb
123
123
  - lib/rspec/tag_matchers.rb
124
124
  - lib/rspec/tag_matchers/has_checkbox.rb
125
+ - lib/rspec/tag_matchers/has_date_select.rb
125
126
  - lib/rspec/tag_matchers/has_form.rb
126
127
  - lib/rspec/tag_matchers/has_input.rb
127
128
  - lib/rspec/tag_matchers/has_select.rb
128
129
  - lib/rspec/tag_matchers/has_tag.rb
129
130
  - lib/rspec/tag_matchers/has_time_select.rb
131
+ - lib/rspec/tag_matchers/helpers/sentence_helper.rb
130
132
  - lib/rspec/tag_matchers/multiple_input_matcher.rb
133
+ - rspec-tag_matchers.gemspec
131
134
  - spec/core_ext/deep_flattening_spec.rb
132
135
  - spec/lib/rspec/tag_matchers/has_checkbox_spec.rb
136
+ - spec/lib/rspec/tag_matchers/has_date_select_spec.rb
133
137
  - spec/lib/rspec/tag_matchers/has_form_spec.rb
134
138
  - spec/lib/rspec/tag_matchers/has_input_spec.rb
135
139
  - spec/lib/rspec/tag_matchers/has_select_spec.rb
@@ -152,7 +156,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
156
  version: '0'
153
157
  segments:
154
158
  - 0
155
- hash: -4097826557927416291
159
+ hash: 95040879081577537
156
160
  required_rubygems_version: !ruby/object:Gem::Requirement
157
161
  none: false
158
162
  requirements: