rspec-html-matchers 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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