rspec-tag_matchers 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "nokogiri"
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem "rspec"
|
7
|
+
gem "yard"
|
8
|
+
gem "bundler"
|
9
|
+
gem "jeweler"
|
10
|
+
gem "guard"
|
11
|
+
gem "guard-rspec"
|
12
|
+
gem "guard-bundler"
|
13
|
+
end
|
14
|
+
|
15
|
+
group :osx do
|
16
|
+
# Needed for Guard to operate optimally on OSX
|
17
|
+
platforms :mri do
|
18
|
+
gem 'rb-fsevent'
|
19
|
+
gem 'growl_notify'
|
20
|
+
end
|
21
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
growl_notify (0.0.3)
|
7
|
+
rb-appscript
|
8
|
+
guard (0.8.8)
|
9
|
+
thor (~> 0.14.6)
|
10
|
+
guard-bundler (0.1.3)
|
11
|
+
bundler (>= 1.0.0)
|
12
|
+
guard (>= 0.2.2)
|
13
|
+
guard-rspec (0.5.2)
|
14
|
+
guard (>= 0.8.4)
|
15
|
+
jeweler (1.6.4)
|
16
|
+
bundler (~> 1.0)
|
17
|
+
git (>= 1.2.5)
|
18
|
+
rake
|
19
|
+
nokogiri (1.5.0)
|
20
|
+
nokogiri (1.5.0-java)
|
21
|
+
rake (0.9.2.2)
|
22
|
+
rb-appscript (0.6.1)
|
23
|
+
rb-fsevent (0.4.3.1)
|
24
|
+
rspec (2.7.0)
|
25
|
+
rspec-core (~> 2.7.0)
|
26
|
+
rspec-expectations (~> 2.7.0)
|
27
|
+
rspec-mocks (~> 2.7.0)
|
28
|
+
rspec-core (2.7.1)
|
29
|
+
rspec-expectations (2.7.0)
|
30
|
+
diff-lcs (~> 1.1.2)
|
31
|
+
rspec-mocks (2.7.0)
|
32
|
+
thor (0.14.6)
|
33
|
+
yard (0.7.3)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
java
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler
|
41
|
+
growl_notify
|
42
|
+
guard
|
43
|
+
guard-bundler
|
44
|
+
guard-rspec
|
45
|
+
jeweler
|
46
|
+
nokogiri
|
47
|
+
rb-fsevent
|
48
|
+
rspec
|
49
|
+
yard
|
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# vim: filetype=ruby
|
2
|
+
|
3
|
+
guard 'bundler' do
|
4
|
+
watch('Gemfile')
|
5
|
+
end
|
6
|
+
|
7
|
+
guard 'rspec', :version => 2, :all_on_start => true, :all_after_pass => true do
|
8
|
+
watch(%r{^spec/.+_spec\.rb$})
|
9
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
10
|
+
watch(%r{^core_ext/(.+)\.rb$}) { |m| "spec/core_ext/#{m[1]}_spec.rb" }
|
11
|
+
watch("spec/spec_helper.rb") { "spec" }
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 David Cuddeback
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
= rspec-tag_matchers
|
2
|
+
|
3
|
+
<tt>rspec-tag_matchers</tt> is a collection of RSpec matchers for testing Rails views. The matchers
|
4
|
+
can match against any string containing HTML or any object whose +to_s+ method returns a string
|
5
|
+
containing HTML (this includes Nokogiri classes). They assume Rails conventions. For example, the
|
6
|
+
+has_form+ matcher looks for a hidden input named +_method+ when testing a form's method.
|
7
|
+
|
8
|
+
== Example
|
9
|
+
|
10
|
+
describe "users/new.html.haml" do
|
11
|
+
before(:each) do
|
12
|
+
assign(:user, User.new)
|
13
|
+
render
|
14
|
+
end
|
15
|
+
|
16
|
+
it { should have_form.with_verb(:post).with_action(user_path) }
|
17
|
+
it { should have_checkbox.for(:user => :terms_of_service) }
|
18
|
+
end
|
19
|
+
|
20
|
+
== Status
|
21
|
+
|
22
|
+
<tt>rspec-tag_matchers</tt> is tested against Ruby 1.8.7, 1.9.2, 1.9.3, REE,
|
23
|
+
Rubinius (1.8 mode) and JRuby.
|
24
|
+
|
25
|
+
{<img src="http://travis-ci.org/dcuddeback/rspec-tag_matchers.png?branch=master" />}[http://travis-ci.org/dcuddeback/rspec-tag_matchers]
|
26
|
+
|
27
|
+
The library is well tested, but the collection of matchers is incomplete. I'm adding more on an
|
28
|
+
as-needed basis. Please feel free to fork this repository to add your own then send me a pull
|
29
|
+
request.
|
30
|
+
|
31
|
+
== Contributing to rspec-tag_matchers
|
32
|
+
|
33
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
34
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
35
|
+
* Fork the project
|
36
|
+
* Start a feature/bugfix branch
|
37
|
+
* Commit and push until you are happy with your contribution
|
38
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
39
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
40
|
+
|
41
|
+
== Copyright
|
42
|
+
|
43
|
+
Copyright (c) 2011 David Cuddeback. See LICENSE.txt for
|
44
|
+
further details.
|
45
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "rspec-tag_matchers"
|
18
|
+
gem.homepage = "http://github.com/dcuddeback/rspec-tag_matchers"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{RSpec matchers for Rails views}
|
21
|
+
gem.description = %Q{A collection of RSpec matchers that understand Rails conventions, allowing for more concise specs.}
|
22
|
+
gem.email = "david.cuddeback@gmail.com"
|
23
|
+
gem.authors = ["David Cuddeback"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :spec
|
35
|
+
|
36
|
+
require 'yard'
|
37
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Mixin for Array and Hash objects that allows deep flattening of nested, heterogeneous data
|
2
|
+
# structures. Since it is only intended for internal use within this library, it is meant to extend
|
3
|
+
# existing Array or Hash objects instead of being included in the Array or Hash classes. This avoids
|
4
|
+
# inconsistencies between test and production environments by not affecting hashes or arrays in the
|
5
|
+
# user's codebase.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# hash = {:foo => {:bar => [1, [2], :baz]}}
|
9
|
+
# hash.respond_to?(:deep_flatten) # => false
|
10
|
+
# hash.extend(DeepFlattening)
|
11
|
+
# hash.respond_to?(:deep_flatten) # => true
|
12
|
+
#
|
13
|
+
# hash.deep_flatten # => [:foo, :bar, 1, 2, :baz]
|
14
|
+
#
|
15
|
+
# array = [1, [2, {:foo => {:bar => [3, 4]}}, 5]
|
16
|
+
# array.respond_to?(:deep_flatten) # => false
|
17
|
+
# array.extend(DeepFlattening)
|
18
|
+
# array.respond_to?(:deep_flatten) # => true
|
19
|
+
#
|
20
|
+
# array.deep_flatten # => [1, 2, :foo, :bar, 3, 4, 5]
|
21
|
+
module DeepFlattening
|
22
|
+
# Flattens a nested, heterogeneous data structure to an array.
|
23
|
+
def deep_flatten
|
24
|
+
# Could be called on an array or a hash. Hashes in 1.9 have a flatten
|
25
|
+
# method, but hashes in 1.8 do not. Therefor, we must call Hash#to_a before
|
26
|
+
# we can flatten the hash in a compatible way.
|
27
|
+
to_a.flatten.map do |object|
|
28
|
+
case object
|
29
|
+
when Array, Hash
|
30
|
+
object.extend(DeepFlattening)
|
31
|
+
object.deep_flatten
|
32
|
+
else
|
33
|
+
object
|
34
|
+
end
|
35
|
+
end.flatten
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RSpec::TagMatchers
|
2
|
+
# Matches <tt><input type="checkbox"></tt> tags in HTML.
|
3
|
+
#
|
4
|
+
# @modifier checked
|
5
|
+
# Specifies that the checkbox must be checked.
|
6
|
+
#
|
7
|
+
# @modifier not_checked
|
8
|
+
# Specifies that the checkbox must *not* be checked.
|
9
|
+
#
|
10
|
+
# @example Matching a checked checkbox for +terms_of_service+
|
11
|
+
# it { should have_checkbox.for(:terms_of_service).checked }
|
12
|
+
#
|
13
|
+
# @example Matching an unchecked checkbox for +terms_of_service+
|
14
|
+
# it { should have_checkbox.for(:terms_of_service).not_checked }
|
15
|
+
#
|
16
|
+
# @return [HasCheckbox]
|
17
|
+
#
|
18
|
+
# @see HasCheckbox.checked
|
19
|
+
# @see HasCheckbox.not_checked
|
20
|
+
def have_checkbox
|
21
|
+
HasCheckbox.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# A matcher that matches <tt><input type="checkbox"></tt> tags.
|
25
|
+
class HasCheckbox < HasInput
|
26
|
+
|
27
|
+
# Initializes a HasCheckbox matcher that matches elements named +input+ with a +type+ attribute
|
28
|
+
# of +checkbox+.
|
29
|
+
def initialize
|
30
|
+
super
|
31
|
+
with_attribute(:type => :checkbox)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Specifies that the checkbox must be selected. A checkbox is considered to be checked if it has
|
35
|
+
# a +checked+ attribute.
|
36
|
+
#
|
37
|
+
# @example Checkboxes which are considered checked
|
38
|
+
# <input type="checkbox" checked="checked" />
|
39
|
+
# <input type="checkbox" checked="1" />
|
40
|
+
# <input type="checkbox" checked />
|
41
|
+
#
|
42
|
+
# @return [HasCheckbox] self
|
43
|
+
def checked
|
44
|
+
with_attribute(:checked => true)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Specifies that the checkbox must *not* be selected. A checkbox is not checked if it has no
|
49
|
+
# +checked+ attribute.
|
50
|
+
#
|
51
|
+
# @example An unchecked checkbox
|
52
|
+
# <input type="checkbox" />
|
53
|
+
#
|
54
|
+
# @return [HasCheckbox] self
|
55
|
+
def not_checked
|
56
|
+
with_attribute(:checked => false)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module RSpec::TagMatchers
|
2
|
+
# Matches +<form>+ tags in HTML.
|
3
|
+
#
|
4
|
+
# @modifier with_verb
|
5
|
+
# Adds a criteria that the form must have the given HTTP verb.
|
6
|
+
#
|
7
|
+
# @modifier with_action
|
8
|
+
# Adds a criteria that the form must target the given URL as its action.
|
9
|
+
#
|
10
|
+
# @example Matching a simple form
|
11
|
+
# it { should have_form.with_verb(:post).with_action("/signup") }
|
12
|
+
#
|
13
|
+
# @example Matching a form with an overridden method
|
14
|
+
# it { should have_form.with_verb(:put) }
|
15
|
+
#
|
16
|
+
# @return [HasForm]
|
17
|
+
#
|
18
|
+
# @see HasForm#with_verb
|
19
|
+
# @see HasForm#with_action
|
20
|
+
def have_form
|
21
|
+
HasForm.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# A matcher that matches +<form>+ tags.
|
25
|
+
class HasForm < HasTag
|
26
|
+
|
27
|
+
# Initializes a HasForm matcher that matches +form+ elements.
|
28
|
+
def initialize
|
29
|
+
super(:form)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Specifies that the form will use the given HTTP verb, following Rails conventions. It first
|
33
|
+
# looks for a method override value (a hidden input named +_method+). If the method override
|
34
|
+
# value doesn't exist, then it looks for the +method+ attribute on the +form+ tag. Whichever
|
35
|
+
# value it finds will be compared to the value passed in as the +verb+ argument to +with_verb+.
|
36
|
+
#
|
37
|
+
# @param [Symbol] verb An HTTP verb, e.g., :get, :post, :put, or :delete.
|
38
|
+
#
|
39
|
+
# @return [HasForm] self
|
40
|
+
def with_verb(verb)
|
41
|
+
with_criteria do |element|
|
42
|
+
matches_verb?(element, verb)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
alias :with_method :with_verb
|
46
|
+
|
47
|
+
# Specifies that the form must target the given URL as its action. It compares the +action+
|
48
|
+
# attribute on the +form+ tag to the +action+ argument passed to +with_action+.
|
49
|
+
#
|
50
|
+
# @param [String, Regexp] action The URL that the form should target.
|
51
|
+
#
|
52
|
+
# @return [HasForm] self
|
53
|
+
def with_action(action)
|
54
|
+
with_attribute(:action => action)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Tests whether +element+ will use the given HTTP +verb+.
|
60
|
+
#
|
61
|
+
# @param [Nokogiri::XML::Node] element A +form+ element to be tested.
|
62
|
+
# @param [Symbol] verb An HTTP verb (:get, :post, :put, etc).
|
63
|
+
#
|
64
|
+
# @return [Boolean]
|
65
|
+
def matches_verb?(element, verb)
|
66
|
+
test_attribute(form_method(element), verb)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns the attribute that specifies which HTTP verb the form will use. It considers a method
|
70
|
+
# override value as well as the +form+ tag's +method+ attribute.
|
71
|
+
#
|
72
|
+
# @param [Nokogiri::XML::Node] element A +form+ element to be tested.
|
73
|
+
#
|
74
|
+
# @return [Nokogiri::XML::Attr] The attribute that specifies the HTTP verb.
|
75
|
+
def form_method(element)
|
76
|
+
method_override(element) || element.attribute("method")
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_override(element)
|
80
|
+
override = element.css("input[type=hidden][name=_method]")
|
81
|
+
override && override.first && override.attribute("value")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'deep_flattening'
|
2
|
+
|
3
|
+
module RSpec::TagMatchers
|
4
|
+
# Matches +<input>+ tags in HTML.
|
5
|
+
#
|
6
|
+
# @modifier for
|
7
|
+
# Adds a criteria that the input must be for the given attribute.
|
8
|
+
#
|
9
|
+
# @example Matching an input for the +name+ attribute on +user+
|
10
|
+
# it { should have_input.for(:user => :name) }
|
11
|
+
# it { should have_input.for(:user, :name) }
|
12
|
+
#
|
13
|
+
# @return [HasInput]
|
14
|
+
#
|
15
|
+
# @see HasInput#for
|
16
|
+
def have_input
|
17
|
+
HasInput.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# A base class for form input matchers.
|
21
|
+
#
|
22
|
+
# == Subclassing
|
23
|
+
#
|
24
|
+
# HasInput is intended to be subclassed to create more expressive matchers for specific types of
|
25
|
+
# form inputs. The helper methods in HasInput, e.g., {#for}, are intended to be useful for all
|
26
|
+
# types of form inputs.
|
27
|
+
#
|
28
|
+
# By default, HasInput matches +<input>+ elements, but this can be overridden by subclasses, e.g.,
|
29
|
+
# HasSelect matches +<select>+ elements. This can be done in the matcher's constructor:
|
30
|
+
#
|
31
|
+
# class HasSelect < HasInput
|
32
|
+
# def initialize
|
33
|
+
# super(:select)
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# Some matchers might want to add more criteria inside their constructors. For example, matchers
|
38
|
+
# that match specific types of +<input>+ tags will want to add a criteria for matching the +type+
|
39
|
+
# attribute:
|
40
|
+
#
|
41
|
+
# class HasCheckbox < HasInput
|
42
|
+
# def initialize
|
43
|
+
# super(:checkbox)
|
44
|
+
# with_attribute(:type => :checkbox)
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
#
|
49
|
+
class HasInput < HasTag
|
50
|
+
|
51
|
+
# Initializes a HasInput matcher that matches elements named +name+.
|
52
|
+
#
|
53
|
+
# @param [Symbol] name The type of HTML tag to match.
|
54
|
+
def initialize(name = :input)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
# Adds a criteria that the input tag should be for a given attribute.
|
59
|
+
#
|
60
|
+
# HasInput provides the {#for} modifier to all of its subclasses, which is useful for matching
|
61
|
+
# the input's name with Rails conventions. For example, a Rails template that uses +form_for+
|
62
|
+
# might output HTML that looks like this:
|
63
|
+
#
|
64
|
+
# <form method="POST" action="/users">
|
65
|
+
# <input type="text" name="user[name]" />
|
66
|
+
# </form>
|
67
|
+
#
|
68
|
+
# Instead of writing:
|
69
|
+
#
|
70
|
+
# it { should have_input.with_attribute(:name => "user[name]") }
|
71
|
+
#
|
72
|
+
# the user can write a more concise spec using {#for}:
|
73
|
+
#
|
74
|
+
# it { should have_input.for(:user => :name) }
|
75
|
+
#
|
76
|
+
# @example Match an input for the +name+ attribute of +user+
|
77
|
+
# it { should have_input.for(:user => :name) }
|
78
|
+
#
|
79
|
+
# @example Match an input for a nested attribute
|
80
|
+
# it { should have_input.for(:user => {:role => :admin}) }
|
81
|
+
# it { should have_input.for(:user, :role => :admin) }
|
82
|
+
#
|
83
|
+
# @param [Array, Hash] args A hierarchy of strings that specifiy the attribute name.
|
84
|
+
#
|
85
|
+
# @return [HasInput] self
|
86
|
+
def for(*args)
|
87
|
+
with_attribute(:name => build_name(*args))
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Converts an array or hash of names to a name for an input form.
|
94
|
+
#
|
95
|
+
# @example
|
96
|
+
# build_name(:foo => :bar) # => "foo[bar]"
|
97
|
+
# build_name(:foo, :bar) # => "foo[bar]"
|
98
|
+
# build_name(:foo => {:bar => :baz}) # => "foo[bar][baz]"
|
99
|
+
# build_name(:foo, :bar => :baz) # => "foo[bar][baz]"
|
100
|
+
#
|
101
|
+
# @param [Array, Hash] args A hierarchy of strings.
|
102
|
+
#
|
103
|
+
# @return [String] The expected name of the form input.
|
104
|
+
def build_name(*args)
|
105
|
+
args.extend(DeepFlattening)
|
106
|
+
args = args.deep_flatten
|
107
|
+
name = args.shift.to_s
|
108
|
+
name + args.map {|piece| "[#{piece}]"}.join
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RSpec::TagMatchers
|
2
|
+
# Matches +<select>+ tags in HTML.
|
3
|
+
#
|
4
|
+
# @example Matching a select tag for a user's role
|
5
|
+
# it { should have_select.for(:user => :role) }
|
6
|
+
#
|
7
|
+
# @return [HasSelect]
|
8
|
+
def have_select
|
9
|
+
HasSelect.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# A matcher that matches +<select>+ tags.
|
13
|
+
class HasSelect < HasInput
|
14
|
+
|
15
|
+
# Initializes a HasSelect matcher that matches +select+ elements.
|
16
|
+
def initialize
|
17
|
+
super(:select)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|