rspec-html-matchers 0.3.5 → 0.4.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/.gitignore CHANGED
@@ -1,12 +1,15 @@
1
+ # gem packaging
1
2
  pkg/*
2
3
  *.gem
3
- .bundle
4
+ # ruby version management
4
5
  .rvmrc
5
6
  .rbenv-*
7
+ # common stuff
8
+ .bundle
6
9
  Gemfile.lock
7
10
  # logs
8
11
  chromedriver.log
9
- # documentation:
12
+ # documentation and other trash
10
13
  html
11
14
  doc
12
15
  .yardoc
@@ -4,8 +4,26 @@ Changelog
4
4
  unreleased(TODO)
5
5
  ----------------
6
6
 
7
- * improve documentation(look at changelog and code!)
8
7
  * add :without to have\_tag? like have_tag('div', :without => { :class => 'test' })
8
+ * with_tag should raise error when used outside have_tag
9
+ * add ability to have_form('/url', 'PUT') or have_form('/url', :PUT)
10
+ * inteligent check comments(make sure it is not searching inside comments)
11
+ * shouldn't show all markup in error message if it is too big
12
+
13
+ 0.5.0(TODO)
14
+ -----------
15
+
16
+ * order matching
17
+ * improve documentation, add more usage examples (look at changelog and code!)
18
+
19
+ 0.4.0
20
+ -----
21
+
22
+ * added with_text matcher
23
+ * some code refactoring, but a lot of refactoring left for future
24
+ * rewritten README, added more usage examples
25
+ * removed dealing with whitespaces (#11), too much magic for usage (#16)
26
+ * some attempt to improve documentation
9
27
 
10
28
  0.3.5
11
29
  -----
data/README.md CHANGED
@@ -1,40 +1,40 @@
1
- Test views, be true! :) [![Build Status](http://travis-ci.org/kucaahbe/rspec-html-matchers.png)](http://travis-ci.org/kucaahbe/rspec-html-matchers)
2
- =======================
1
+ rspec-html-matchers
2
+ ===================
3
3
 
4
- [![Mikhalok](https://github.com/kucaahbe/rspec-html-matchers/raw/master/mikhalok.jpg)](http://www.myspace.com/lyapis "Lyapis Trubetskoy ska-punk band")
4
+ [RSpec 2](https://www.relishapp.com/rspec) matchers for testing your html.
5
5
 
6
- Why?
7
- ===
6
+ [![Build Status](http://travis-ci.org/kucaahbe/rspec-html-matchers.png)](http://travis-ci.org/kucaahbe/rspec-html-matchers)
8
7
 
9
- * you need to test some complex views
10
- * and you want to use RSpec2
11
- * and assert\_select seems is something strange to you
12
- * have_tag in [rspec-rails](http://github.com/rspec/rspec-rails) are deprecated now
13
- * you need a user-firendly output in your error messages
8
+ Goals
9
+ -----
14
10
 
15
- Being true
16
- ==========
11
+ * for testing **complicated** html output, for simple matching consider use:
12
+ * [assert_select](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/SelectorAssertions.html#method-i-assert_select)
13
+ * [matchers provided out of the box in rspec-rails](https://www.relishapp.com/rspec/rspec-rails/v/2-11/docs/view-specs/view-spec)
14
+ * [matchers provided by capybara](http://rdoc.info/github/jnicklas/capybara/Capybara/Node/Matchers)
15
+ * developer-firendly output in error messages
16
+ * built on top of [nokogiri](nokogiri.org)
17
+ * has support for [capybara](https://github.com/jnicklas/capybara), see below
18
+ * syntax is similar to [have_tag](http://old.rspec.info/rails/writing/views.html) matcher from old-school rspec-rails, but with own syntactic sugar
19
+ * framework agnostic, as input should be String(or capybara's page, see below)
17
20
 
18
21
  Install
19
22
  -------
20
23
 
21
- Add to your Gemfile (in the :test group :) ):
24
+ Add to your Gemfile in the `:test` group:
22
25
 
23
26
  ```ruby
24
- group :test do
25
- gem 'rspec-html-matchers'
26
- end
27
+ gem 'rspec-html-matchers'
27
28
  ```
28
29
 
29
- Instructions for [installing Nokogiri](http://nokogiri.org/tutorials/installing_nokogiri.html).
30
+ as this gem requires **nokogiri**, here [instructions for installing it](http://nokogiri.org/tutorials/installing_nokogiri.html).
30
31
 
31
32
  Usage
32
33
  -----
33
34
 
34
- Simple example:
35
+ so perharps your code produces following output:
35
36
 
36
- ```ruby
37
- view=<<-HTML
37
+ ```html
38
38
  <h1>Simple Form</h1>
39
39
  <form action="/users" method="post">
40
40
  <p>
@@ -44,9 +44,12 @@ view=<<-HTML
44
44
  <input type="submit" id="special_submit" />
45
45
  </p>
46
46
  </form>
47
- HTML
47
+ ```
48
+
49
+ so you test it with following:
48
50
 
49
- view.should have_tag('form', :with => { :action => '/users', :method => 'post' }) do
51
+ ```ruby
52
+ rendered.should have_tag('form', :with => { :action => '/users', :method => 'post' }) do
50
53
  with_tag "input", :with => { :name => "user[email]", :type => 'email' }
51
54
  with_tag "input#special_submit", :count => 1
52
55
  without_tag "h1", :text => 'unneeded tag'
@@ -54,76 +57,103 @@ view.should have_tag('form', :with => { :action => '/users', :method => 'post' }
54
57
  end
55
58
  ```
56
59
 
57
- Examples with more description:
58
-
59
- * tag matching (matches one or more tags):
60
-
61
- ```ruby
62
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p')
63
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag(:p)
64
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p#qwerty')
65
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p.qwe.rty')
66
-
67
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p strong')
68
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p#qwerty strong')
69
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p.qwe.rty strong')
70
- # or
71
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p') do
72
- with_tag('strong')
73
- end
74
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p#qwerty') do
75
- with_tag('strong')
76
- end
77
- '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p.qwe.rty') do
78
- with_tag('strong')
79
- end
80
- ```
81
-
82
- * special case: classes matching:
83
-
84
- ```ruby
85
- # all of this are equivalent:
86
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => 'qwe rty' })
87
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => 'rty qwe' })
88
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => ['rty', 'qwe'] })
89
- '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => ['qwe', 'rty'] })
90
- ```
91
-
92
- usage with capybara and cucumber:
93
-
94
- page.should have_tag( ... )
60
+ Example about should be self-descriptive, but if not refer to [have_tag](http://rdoc.info/github/kucaahbe/rspec-html-matchers/RSpec/Matchers:have_tag) documentation
61
+
62
+ Input could be any html string. Let's take a look at these examples:
63
+
64
+ * matching tags by css:
65
+
66
+ ```ruby
67
+ # simple examples:
68
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p')
69
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag(:p)
70
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p#qwerty')
71
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p.qwe.rty')
72
+ # more complicated examples:
73
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p strong')
74
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p#qwerty strong')
75
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p.qwe.rty strong')
76
+ # or you can use another syntax for examples above
77
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p') do
78
+ with_tag('strong')
79
+ end
80
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p#qwerty') do
81
+ with_tag('strong')
82
+ end
83
+ '<p class="qwe rty" id="qwerty"><strong>Para</strong>graph</p>'.should have_tag('p.qwe.rty') do
84
+ with_tag('strong')
85
+ end
86
+ ```
87
+
88
+ * special case for classes matching:
89
+
90
+ ```ruby
91
+ # all of this are equivalent:
92
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => 'qwe rty' })
93
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => 'rty qwe' })
94
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => ['rty', 'qwe'] })
95
+ '<p class="qwe rty" id="qwerty">Paragraph</p>'.should have_tag('p', :with => { :class => ['qwe', 'rty'] })
96
+ ```
97
+
98
+ * content matching:
99
+
100
+ ```ruby
101
+ '<p> Some content&nbsphere</p>'.should have_tag('p', :text => ' Some content here')
102
+ # or
103
+ '<p> Some content&nbsphere</p>'.should have_tag('p') do
104
+ with_text ' Some content here'
105
+ end
106
+
107
+ '<p> Some content&nbsphere</p>'.should have_tag('p', :text => /Some content here/)
108
+ # or
109
+ '<p> Some content&nbsphere</p>'.should have_tag('p') do
110
+ with_text /Some content here/
111
+ end
112
+
113
+ # mymock.text == 'Some content here'
114
+ '<p> Some content&nbsphere</p>'.should have_tag('p', :content => mymock.text)
115
+ # or
116
+ '<p> Some content&nbsphere</p>'.should have_tag('p') do
117
+ with_content mymock.text
118
+ end
119
+ ```
120
+
121
+ * usage with capybara and cucumber:
122
+
123
+ ```ruby
124
+ page.should have_tag( ... )
125
+ ```
95
126
 
96
127
  where `page` is an instance of Capybara::Session
97
128
 
98
- Also included special matchers for form inputs:
99
- -----------------------------------------------
100
-
101
- - [have\_form](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:have_form)
102
- - [with\_checkbox](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_checkbox)
103
- - [with\_email\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_email_field)
104
- - [with\_file\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_file_field)
105
- - [with\_hidden\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_hidden_field)
106
- - [with\_option](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_option)
107
- - [with\_password_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_password_field)
108
- - [with\_radio\_button](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_radio_button)
109
- - [with\_button](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_button)
110
- - [with\_select](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_select)
111
- - [with\_submit](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_submit)
112
- - [with\_text\_area](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_text_area)
113
- - [with\_text\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_text_field)
114
- - [with\_url\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_url_field)
115
- - [with\_number\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_number_field)
116
- - [with\_range\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_range_field)
117
- - [with\_date\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_date_field)
129
+ * also included shorthand matchers for form inputs:
130
+
131
+ - [have\_form](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:have_form)
132
+ - [with\_checkbox](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_checkbox)
133
+ - [with\_email\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_email_field)
134
+ - [with\_file\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_file_field)
135
+ - [with\_hidden\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_hidden_field)
136
+ - [with\_option](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_option)
137
+ - [with\_password_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_password_field)
138
+ - [with\_radio\_button](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_radio_button)
139
+ - [with\_button](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_button)
140
+ - [with\_select](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_select)
141
+ - [with\_submit](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_submit)
142
+ - [with\_text\_area](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_text_area)
143
+ - [with\_text\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_text_field)
144
+ - [with\_url\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_url_field)
145
+ - [with\_number\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_number_field)
146
+ - [with\_range\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_range_field)
147
+ - [with\_date\_field](http://rdoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers:with_date_field)
118
148
 
119
149
  and of course you can use the `without_` matchers (see the documentation).
120
150
 
121
151
  More info
122
152
  ---------
123
153
 
124
- You can find more on [RubyDoc](http://rubydoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers), take a look at {RSpec::Matchers#have\_tag have\_tag} method.
154
+ You can find more on [RubyDoc](http://rubydoc.info/github/kucaahbe/rspec-html-matchers/master/RSpec/Matchers), take a look at [have_tag](http://rdoc.info/github/kucaahbe/rspec-html-matchers/RSpec/Matchers#have_tag-instance_method) method.
125
155
 
126
- Also, please read {file:CHANGELOG.md CHANGELOG}, it might be helpful.
156
+ Also, please read [CHANGELOG](https://github.com/kucaahbe/rspec-html-matchers/blob/master/CHANGELOG.md), it might be helpful.
127
157
 
128
158
  Contribution
129
159
  ============
@@ -131,7 +161,8 @@ Contribution
131
161
  1. Fork the repository
132
162
  2. Add tests for your feature
133
163
  3. Write the code
134
- 4. Send a pull request
164
+ 4. Add documentation for your contribution
165
+ 5. Send a pull request
135
166
 
136
167
  Contributors
137
168
  ============
@@ -141,7 +172,7 @@ Contributors
141
172
  - [Simon Schoeters](https://github.com/cimm)
142
173
  - [Felix Tjandrawibawa](https://github.com/cemenghttps://github.com/cemeng)
143
174
  - [Szymon Przybył](https://github.com/apocalyptiq)
144
- - [Howard Wilson][https://github.com/watsonbox]
175
+ - [Howard Wilson](https://github.com/watsonbox)
145
176
 
146
177
  MIT Licensed
147
178
  ============
data/Rakefile CHANGED
@@ -3,19 +3,18 @@ require 'rspec/core/rake_task'
3
3
  require 'cucumber/rake/task'
4
4
  Bundler::GemHelper.install_tasks
5
5
 
6
- gemspec = eval(File.read(Dir["*.gemspec"].first))
7
-
8
6
  task :default => [:spec, :cucumber]
9
7
 
10
- RSpec::Core::RakeTask.new(:spec) do |t|
11
- t.rspec_opts='--tag ~wip'
12
- end
13
-
14
8
  desc "Validate the gemspec"
15
9
  task :gemspec do
10
+ gemspec = eval(File.read(Dir["*.gemspec"].first))
16
11
  gemspec.validate && puts('gemspec valid')
17
12
  end
18
13
 
14
+ RSpec::Core::RakeTask.new(:spec) do |t|
15
+ t.rspec_opts='--tag ~wip'
16
+ end
17
+
19
18
  namespace :spec do
20
19
  RSpec::Core::RakeTask.new(:wip) do |t|
21
20
  t.rspec_opts='--tag wip'
@@ -2,7 +2,7 @@ require 'sinatra/base'
2
2
  require 'capybara/cucumber'
3
3
  require 'rspec-html-matchers'
4
4
 
5
- $ASSETS_DIR = File.join(Dir.pwd,'assets')
5
+ $ASSETS_DIR = File.expand_path('../../../spec/assets',__FILE__)
6
6
  $INDEX_HTML = File.join($ASSETS_DIR,'index.html')
7
7
 
8
8
  class SimpleApp < Sinatra::Base
@@ -4,6 +4,43 @@ require 'nokogiri'
4
4
  module RSpec
5
5
  module Matchers
6
6
 
7
+ # @api
8
+ # @private
9
+ # for nokogiri regexp matching
10
+ class NokogiriRegexpHelper
11
+ def initialize(regex)
12
+ @regex = regex
13
+ end
14
+
15
+ def regexp node_set
16
+ node_set.find_all { |node| node.content =~ @regex }
17
+ end
18
+ end
19
+
20
+ # @api
21
+ # @private
22
+ class NokogiriTextHelper
23
+ def initialize text
24
+ @text = text
25
+ end
26
+
27
+ def content node_set
28
+ match_text = @text.gsub(/\\000027/, "'")
29
+ node_set.find_all do |node|
30
+ actual_content = node.content
31
+ # remove non-breaking spaces:
32
+ case RUBY_VERSION
33
+ when /^1\.9/
34
+ actual_content.gsub!(/\u00a0/, ' ')
35
+ when /^1\.8/
36
+ actual_content.gsub!("\302\240", ' ')
37
+ end
38
+
39
+ actual_content == match_text
40
+ end
41
+ end
42
+ end
43
+
7
44
  # @api
8
45
  # @private
9
46
  class NokogiriMatcher
@@ -30,8 +67,6 @@ module RSpec
30
67
  MIN_MAX_ERROR_MSG = %Q|:minimum shold be less than :maximum!|
31
68
  BAD_RANGE_ERROR_MSG = %Q|Your :count range(%s) has no sence!|
32
69
 
33
- PRESERVE_WHITESPACE_TAGS = %w( pre textarea )
34
-
35
70
  def initialize tag, options={}, &block
36
71
  @tag, @options, @block = tag.to_s, options, block
37
72
 
@@ -68,7 +103,7 @@ module RSpec
68
103
  @document = @parent_scope.to_html
69
104
  end
70
105
 
71
- if tag_presents? and content_right? and count_right?
106
+ if tag_presents? and text_right? and count_right?
72
107
  @current_scope = @parent_scope
73
108
  @block.call if @block
74
109
  true
@@ -116,20 +151,12 @@ module RSpec
116
151
  end
117
152
  end
118
153
 
119
- def content_right?
154
+ def text_right?
120
155
  return true unless @options[:text]
121
156
 
122
157
  case text=@options[:text]
123
158
  when Regexp
124
- new_scope = @current_scope.css(":regexp()",Class.new {
125
- def initialize(regex)
126
- @regex = regex
127
- end
128
-
129
- def regexp node_set
130
- node_set.find_all { |node| node.content =~ @regex }
131
- end
132
- }.new(text))
159
+ new_scope = @current_scope.css(':regexp()',NokogiriRegexpHelper.new(text))
133
160
  unless new_scope.empty?
134
161
  @count = new_scope.count
135
162
  @negative_failure_message = REGEXP_FOUND_MSG % [text.inspect,@tag,@document]
@@ -139,23 +166,7 @@ module RSpec
139
166
  false
140
167
  end
141
168
  else
142
- css_param = text.gsub(/'/) { %q{\000027} }
143
- new_scope = @current_scope.css(":content('#{css_param}')",Class.new {
144
- def content node_set, text
145
- match_text = text.gsub(/\\000027/, "'")
146
- node_set.find_all do |node|
147
- actual_content = if PRESERVE_WHITESPACE_TAGS.include?(node.name)
148
- node.content
149
- else
150
- node.content.strip.squeeze(' ')
151
- end
152
- # remove non-braking spaces:
153
- actual_content.gsub!("\u00a0", ' ')
154
- actual_content.gsub!("\302\240", ' ')
155
- actual_content == match_text
156
- end
157
- end
158
- }.new)
169
+ new_scope = @current_scope.css(':content()',NokogiriTextHelper.new(text))
159
170
  unless new_scope.empty?
160
171
  @count = new_scope.count
161
172
  @negative_failure_message = TEXT_FOUND_MSG % [text,@tag,@document]
@@ -200,20 +211,20 @@ module RSpec
200
211
 
201
212
  end
202
213
 
203
- # have_tag matcher
214
+ # tag assertion, this is the core of functionality, other matchers are shortcuts to this matcher
204
215
  #
205
- # @yield block where you should put with_tag
216
+ # @yield block where you should put with_tag, without_tag and/or other matchers
206
217
  #
207
- # @param [String] tag css selector for tag you want to match
218
+ # @param [String] tag css selector for tag you want to match, e.g. 'div', 'section#my', 'article.red'
208
219
  # @param [Hash] options options hash(see below)
209
- # @option options [Hash] :with hash with other attributes, within this, key :class have special meaning, you may specify it as array of expected classes or string of classes separated by spaces, order does not matter
210
- # @option options [Fixnum] :count count of matched tags(DO NOT USE :count with :minimum(:min) or :maximum(:max)*)
211
- # @option options [Range] :count count of matched tags should be between range minimum and maximum values
220
+ # @option options [Hash] :with hash with html attributes, within this, *:class* option have special meaning, you may specify it as array of expected classes or string of classes separated by spaces, order does not matter
221
+ # @option options [Fixnum] :count for tag count matching(*ATTENTION:* do not use :count with :minimum(:min) or :maximum(:max))
222
+ # @option options [Range] :count not strict tag count matching, count of tags should be in specified range
212
223
  # @option options [Fixnum] :minimum minimum count of elements to match
213
224
  # @option options [Fixnum] :min same as :minimum
214
225
  # @option options [Fixnum] :maximum maximum count of elements to match
215
226
  # @option options [Fixnum] :max same as :maximum
216
- #
227
+ # @option options [String/Regexp] :text to match tag content, could be either String or Regexp
217
228
  #
218
229
  # @example
219
230
  # rendered.should have_tag('div')
@@ -221,7 +232,7 @@ module RSpec
221
232
  # rendered.should have_tag('div#footer')
222
233
  # rendered.should have_tag('input#email', :with => { :name => 'user[email]', :type => 'email' } )
223
234
  # rendered.should have_tag('div', :count => 3) # matches exactly 3 'div' tags
224
- # rendered.should have_tag('div', :count => 3..7) # something like have_tag('div', :minimum => 3, :maximum => 7)
235
+ # rendered.should have_tag('div', :count => 3..7) # shortcut for have_tag('div', :minimum => 3, :maximum => 7)
225
236
  # rendered.should have_tag('div', :minimum => 3) # matches more(or equal) than 3 'div' tags
226
237
  # rendered.should have_tag('div', :maximum => 3) # matches less(or equal) than 3 'div' tags
227
238
  # rendered.should have_tag('p', :text => 'some content') # will match "<p>some content</p>"
@@ -235,14 +246,28 @@ module RSpec
235
246
  # '<div class="one two">'.should have_tag('div', :with => { :class => ['two', 'one'] })
236
247
  # '<div class="one two">'.should have_tag('div', :with => { :class => 'two one' })
237
248
  def have_tag tag, options={}, &block
238
- if options.kind_of? String
239
- options = { :text => options }
240
- end
249
+ # for backwards compatibility with rpecs have tag:
250
+ options = { :text => options } if options.kind_of? String
241
251
  @__current_scope_for_nokogiri_matcher = NokogiriMatcher.new(tag, options, &block)
242
252
  end
243
253
 
254
+ def with_text text
255
+ raise StandardError, 'this matcher should be used inside "have_tag" matcher block' unless defined?(@__current_scope_for_nokogiri_matcher)
256
+ raise ArgumentError, 'this matcher does not accept block' if block_given?
257
+ tag = @__current_scope_for_nokogiri_matcher.instance_variable_get(:@tag)
258
+ @__current_scope_for_nokogiri_matcher.should have_tag(tag, :text => text)
259
+ end
260
+
261
+ def without_text text
262
+ raise StandardError, 'this matcher should be used inside "have_tag" matcher block' unless defined?(@__current_scope_for_nokogiri_matcher)
263
+ raise ArgumentError, 'this matcher does not accept block' if block_given?
264
+ tag = @__current_scope_for_nokogiri_matcher.instance_variable_get(:@tag)
265
+ @__current_scope_for_nokogiri_matcher.should_not have_tag(tag, :text => text)
266
+ end
267
+ alias :but_without_text :without_text
268
+
244
269
  # with_tag matcher
245
- # @yield
270
+ # @yield block where you should put other with_tag or without_tag
246
271
  # @see #have_tag
247
272
  # @note this should be used within block of have_tag matcher
248
273
  def with_tag tag, options={}, &block
@@ -250,13 +275,19 @@ module RSpec
250
275
  end
251
276
 
252
277
  # without_tag matcher
253
- # @yield
278
+ # @yield block where you should put other with_tag or without_tag
254
279
  # @see #have_tag
255
280
  # @note this should be used within block of have_tag matcher
256
281
  def without_tag tag, options={}, &block
257
282
  @__current_scope_for_nokogiri_matcher.should_not have_tag(tag, options, &block)
258
283
  end
259
284
 
285
+ # form assertion
286
+ #
287
+ # it is a shortcut to
288
+ # have_tag 'form', :with => { :action => action_url, :method => method ... }
289
+ # @yield block with with_<field>, see below
290
+ # @see #have_tag
260
291
  def have_form action_url, method, options={}, &block
261
292
  options[:with] ||= {}
262
293
  id = options[:with].delete(:id)
@@ -266,63 +297,55 @@ module RSpec
266
297
  have_tag tag, options, &block
267
298
  end
268
299
 
300
+ #TODO fix code duplications
301
+
269
302
  def with_hidden_field name, value=nil
270
- options = { :with => { :name => name, :type => 'hidden' } }
271
- options[:with].merge!(:value => value) if value
303
+ options = form_tag_options('hidden',name,value)
272
304
  should_have_input(options)
273
305
  end
274
306
 
275
307
  def without_hidden_field name, value=nil
276
- options = { :with => { :name => name, :type => 'hidden' } }
277
- options[:with].merge!(:value => value) if value
308
+ options = form_tag_options('hidden',name,value)
278
309
  should_not_have_input(options)
279
310
  end
280
311
 
281
312
  def with_text_field name, value=nil
282
- options = { :with => { :name => name, :type => 'text' } }
283
- options[:with].merge!(:value => value) if value
313
+ options = form_tag_options('text',name,value)
284
314
  should_have_input(options)
285
315
  end
286
316
 
287
317
  def without_text_field name, value=nil
288
- options = { :with => { :name => name, :type => 'text' } }
289
- options[:with].merge!(:value => value) if value
318
+ options = form_tag_options('text',name,value)
290
319
  should_not_have_input(options)
291
320
  end
292
321
 
293
322
  def with_email_field name, value=nil
294
- options = { :with => { :name => name, :type => 'email' } }
295
- options[:with].merge!(:value => value) if value
323
+ options = form_tag_options('email',name,value)
296
324
  should_have_input(options)
297
325
  end
298
326
 
299
327
  def without_email_field name, value=nil
300
- options = { :with => { :name => name, :type => 'email' } }
301
- options[:with].merge!(:value => value) if value
328
+ options = form_tag_options('email',name,value)
302
329
  should_not_have_input(options)
303
330
  end
304
331
 
305
332
  def with_url_field name, value=nil
306
- options = { :with => { :name => name, :type => 'url' } }
307
- options[:with].merge!(:value => value) if value
333
+ options = form_tag_options('url',name,value)
308
334
  should_have_input(options)
309
335
  end
310
336
 
311
337
  def without_url_field name, value=nil
312
- options = { :with => { :name => name, :type => 'url' } }
313
- options[:with].merge!(:value => value) if value
338
+ options = form_tag_options('url',name,value)
314
339
  should_not_have_input(options)
315
340
  end
316
341
 
317
342
  def with_number_field name, value=nil
318
- options = { :with => { :name => name, :type => 'number' } }
319
- options[:with].merge!(:value => value.to_s) if value
343
+ options = form_tag_options('number',name,value)
320
344
  should_have_input(options)
321
345
  end
322
346
 
323
347
  def without_number_field name, value=nil
324
- options = { :with => { :name => name, :type => 'number' } }
325
- options[:with].merge!(:value => value.to_s) if value
348
+ options = form_tag_options('number',name,value)
326
349
  should_not_have_input(options)
327
350
  end
328
351
 
@@ -356,57 +379,55 @@ module RSpec
356
379
  should_not_have_input(options)
357
380
  end
358
381
 
359
- def with_password_field name
360
- options = { :with => { :name => name, :type => 'password' } }
382
+ def with_password_field name, value=nil
383
+ options = form_tag_options('password',name,value)
361
384
  should_have_input(options)
362
385
  end
363
386
 
364
- def without_password_field name
365
- options = { :with => { :name => name, :type => 'password' } }
387
+ def without_password_field name, value=nil
388
+ options = form_tag_options('password',name,value)
366
389
  should_not_have_input(options)
367
390
  end
368
391
 
369
- def with_file_field name
370
- options = { :with => { :name => name, :type => 'file' } }
392
+ def with_file_field name, value=nil
393
+ options = form_tag_options('file',name,value)
371
394
  should_have_input(options)
372
395
  end
373
396
 
374
- def without_file_field name
375
- options = { :with => { :name => name, :type => 'file' } }
397
+ def without_file_field name, value=nil
398
+ options = form_tag_options('file',name,value)
376
399
  should_not_have_input(options)
377
400
  end
378
401
 
379
- def with_text_area name
402
+ def with_text_area name#TODO, text=nil
403
+ #options = form_tag_options('text',name,value)
380
404
  options = { :with => { :name => name } }
381
405
  @__current_scope_for_nokogiri_matcher.should have_tag('textarea', options)
382
406
  end
383
407
 
384
- def without_text_area name
408
+ def without_text_area name#TODO, text=nil
409
+ #options = form_tag_options('text',name,value)
385
410
  options = { :with => { :name => name } }
386
411
  @__current_scope_for_nokogiri_matcher.should_not have_tag('textarea', options)
387
412
  end
388
413
 
389
414
  def with_checkbox name, value=nil
390
- options = { :with => { :name => name, :type => 'checkbox' } }
391
- options[:with].merge!(:value => value) if value
415
+ options = form_tag_options('checkbox',name,value)
392
416
  should_have_input(options)
393
417
  end
394
418
 
395
419
  def without_checkbox name, value=nil
396
- options = { :with => { :name => name, :type => 'checkbox' } }
397
- options[:with].merge!(:value => value) if value
420
+ options = form_tag_options('checkbox',name,value)
398
421
  should_not_have_input(options)
399
422
  end
400
423
 
401
424
  def with_radio_button name, value
402
- options = { :with => { :name => name, :type => 'radio' } }
403
- options[:with].merge!(:value => value)
425
+ options = form_tag_options('radio',name,value)
404
426
  should_have_input(options)
405
427
  end
406
428
 
407
429
  def without_radio_button name, value
408
- options = { :with => { :name => name, :type => 'radio' } }
409
- options[:with].merge!(:value => value)
430
+ options = form_tag_options('radio',name,value)
410
431
  should_not_have_input(options)
411
432
  end
412
433
 
@@ -482,10 +503,12 @@ module RSpec
482
503
 
483
504
  def with_submit value
484
505
  options = { :with => { :type => 'submit', :value => value } }
506
+ #options = form_tag_options('text',name,value)
485
507
  should_have_input(options)
486
508
  end
487
509
 
488
510
  def without_submit value
511
+ #options = form_tag_options('text',name,value)
489
512
  options = { :with => { :type => 'submit', :value => value } }
490
513
  should_not_have_input(options)
491
514
  end
@@ -500,5 +523,13 @@ module RSpec
500
523
  @__current_scope_for_nokogiri_matcher.should_not have_tag('input', options)
501
524
  end
502
525
 
526
+ # form_tag in method name name mean smth. like input, submit, tags that should appear in a form
527
+ def form_tag_options form_tag_type, form_tag_name, form_tag_value=nil
528
+ options = { :with => { :name => form_tag_name, :type => form_tag_type } }
529
+ # .to_s if value is a digit or smth. else, see issue#10
530
+ options[:with].merge!(:value => form_tag_value.to_s) if form_tag_value
531
+ return options
532
+ end
533
+
503
534
  end
504
535
  end