rspec-html-matchers 0.8.1 → 0.9.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 22c01ae28fa7cad1439746a9dd405d82c9e1d03b
4
- data.tar.gz: 82a8b327805ab2b26e6bb8a06485d4b65442bda2
2
+ SHA256:
3
+ metadata.gz: c80050c4efaf8917fde81c8f5b139f846731b679f1a9001fec4a3e49690451b3
4
+ data.tar.gz: bc7e5523735f253a51fff1055e364f1189d7daa5dcedacf388612c4a57889e5a
5
5
  SHA512:
6
- metadata.gz: 745e59864f28ec8bf28ec3ccef9da23c22d8bc03b3c8e4220412c54ec889b2cac59b32c2229051da5783c8be04f0443196324422f96c476292a6a98c0b2edc97
7
- data.tar.gz: 097dcf96e8f5680a6dc45cd293909ea0e20e83e7b1c96694f71849588bb5b8ab9c7334e782e4f42f938d5753d47d98aafbed6ce84c2e945bcaa51cf4ea9f2e26
6
+ metadata.gz: 2febc5b59c123bd2e9232aecfb7839a047f29dbd0b30c0958378523bc04f3c119ad60923a7535def8c2748bb5f92793b47707a9db16ff864dc20696f7e8985d0
7
+ data.tar.gz: 38c6bf3867215196fd61a4877f8cabbd6bade0dc66ae4ae1edb00945b9d0ac6deecdbcdc0101530255347f6647c1e619f540a4cfe3fb6ca84d807fbf735b7ac6
@@ -1,15 +1,48 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- unreleased(TODO)
5
- ----------------
4
+ 0.9.4
5
+ -----
6
+ * html/body matching from now is forbidden ([#75](https://github.com/kucaahbe/rspec-html-matchers/pull/75))
7
+ * make ruby 2.7 possible to fail on CI
8
+
9
+ 0.9.3
10
+ -----
11
+ * fix for :seen option ([#73](https://github.com/kucaahbe/rspec-html-matchers/issues/73))
12
+ * fix for html/body matching ([#62](https://github.com/kucaahbe/rspec-html-matchers/issues/62))
13
+ * a bit of linting and refactoring
14
+
15
+ 0.9.2
16
+ -----
17
+
18
+ * fixed have_empty_tag matcher on jruby
19
+ * added ruby 2.7 to build matrix (no official support until stable release though)
20
+ * added ruby 2.6 to build matrix ([#70](https://github.com/kucaahbe/rspec-html-matchers/pull/70))
21
+
22
+ infrastructure/misc:
23
+
24
+ * migrated to travis-ci.com instead of travis-ci.org (thanks [Hamada Takayuki](https://github.com/hamadata) for inspiration: [#70](https://github.com/kucaahbe/rspec-html-matchers/pull/70))
25
+ * refactor travis-ci build setup
26
+ * updated status badges to SVG (thanks to [Olle Jonsson](https://github.com/olleolleolle): [#71](https://github.com/kucaahbe/rspec-html-matchers/pull/71))
27
+
28
+ 0.9.1
29
+ -----
30
+
31
+ * re-added ruby support back to 1.8.7 (supporting same versions as rspec 3)
32
+ * split lib in few files
33
+
34
+ 0.9.0
35
+ -----
36
+
37
+ * fixed `with_tag` nesting (thanks to [randoum](https://github.com/randoum): [#59](https://github.com/kucaahbe/rspec-html-matchers/pull/59))
38
+ * added ~> 2.4 ruby support
39
+ * removed ~> 2 ruby support
40
+
41
+ 0.8.2
42
+ -----
6
43
 
7
- * with_tag should raise error when used outside have_tag
8
- * add ability to have_form('/url', 'PUT') or have_form('/url', :PUT)
9
- * inteligent check comments(make sure it is not searching inside comments)
10
- * shouldn't show all markup in error message if it is too big
11
- * order matching
12
- * improve documentation, add more usage examples (look at changelog and code!)
44
+ * fixed README (thanks to [Rodrigo Castro](https://github.com/roooodcastro))
45
+ * fixed deep nesting (thanks to [Misha Gorodnitzky](https://github.com/misaka))
13
46
 
14
47
  0.8.1
15
48
  -----
@@ -62,7 +95,7 @@ unreleased(TODO)
62
95
  0.4.4
63
96
  -----
64
97
 
65
- * options for have_tag now support Regexp (thanks to [Ian C. Anderson](http://github.com/iancanderson))
98
+ * options for have_tag now support Regexp (thanks to [Ian C. Anderson](https://github.com/iancanderson))
66
99
 
67
100
  0.4.3
68
101
  -----
@@ -111,14 +144,14 @@ unreleased(TODO)
111
144
  0.2.2
112
145
  -----
113
146
 
114
- * leading and trailing whitespaces are ignored in tags where they should be ignored(#11, and again thanks to [Simon Schoeters](http://github.com/cimm))
147
+ * leading and trailing whitespaces are ignored in tags where they should be ignored(#11, and again thanks to [Simon Schoeters](https://github.com/cimm))
115
148
  * whitespaces ignoring as browser does in :text matching
116
149
  * have_tag backwards compability(thanks to [Felix Tjandrawibawa](https://github.com/cemeng), #12)
117
150
 
118
151
  0.2.1
119
152
  -----
120
153
 
121
- * make possible use non-string as :text option(#10, thanks for idea to [Simon Schoeters](http://github.com/cimm))
154
+ * make possible use non-string as :text option(#10, thanks for idea to [Simon Schoeters](https://github.com/cimm))
122
155
 
123
156
  0.2.0
124
157
  -----
@@ -137,7 +170,7 @@ unreleased(TODO)
137
170
  0.0.6
138
171
  -----
139
172
 
140
- * allow for single quotes in content matchers (thanks to [Kelly Felkins](http://github.com/kellyfelkins)).
173
+ * allow for single quotes in content matchers (thanks to [Kelly Felkins](https://github.com/kellyfelkins)).
141
174
 
142
175
  0.0.5 (trial-trip)
143
176
  ------------------
data/README.md CHANGED
@@ -1,20 +1,16 @@
1
- rspec-html-matchers
2
- ===================
1
+ # rspec-html-matchers [![Gem Version](https://badge.fury.io/rb/rspec-html-matchers.svg)](https://badge.fury.io/rb/rspec-html-matchers) [![Build Status](https://travis-ci.com/kucaahbe/rspec-html-matchers.svg?branch=master)](https://travis-ci.com/kucaahbe/rspec-html-matchers)
3
2
 
4
3
  [RSpec 3](https://www.relishapp.com/rspec) matchers for testing your html (for [RSpec 2](https://www.relishapp.com/rspec/rspec-core/v/2-99/docs) use 0.5.x version).
5
4
 
6
- [![Gem Version](https://badge.fury.io/rb/rspec-html-matchers.png)](http://badge.fury.io/rb/rspec-html-matchers)
7
- [![Build Status](https://travis-ci.org/kucaahbe/rspec-html-matchers.png)](http://travis-ci.org/kucaahbe/rspec-html-matchers)
8
-
9
5
  Goals
10
6
  -----
11
7
 
12
- * for testing **complicated** html output, for simple matching consider use:
13
- * [assert_select](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/SelectorAssertions.html#method-i-assert_select)
8
+ * designed for testing **complex** html output. If you plan to perform simple matching, consider using:
9
+ * [assert_select](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/SelectorAssertions.html#method-i-assert_select)
14
10
  * [matchers provided out of the box in rspec-rails](https://www.relishapp.com/rspec/rspec-rails/v/2-11/docs/view-specs/view-spec)
15
- * [matchers provided by capybara](http://rdoc.info/github/jnicklas/capybara/Capybara/Node/Matchers)
11
+ * [matchers provided by capybara](https://rdoc.info/github/jnicklas/capybara/Capybara/Node/Matchers)
16
12
  * developer-friendly output in error messages
17
- * built on top of [nokogiri](http://www.nokogiri.org/)
13
+ * built on top of [nokogiri](https://www.nokogiri.org/)
18
14
  * has support for [capybara](https://github.com/jnicklas/capybara), see below
19
15
  * syntax is similar to `have_tag` matcher from rspec-rails 1.x, but with own syntactic sugar
20
16
  * framework agnostic, as input should be `String` (or capybara's page, see below)
@@ -45,7 +41,6 @@ describe "my view spec" do
45
41
  it "has tags" do
46
42
  expect(rendered).to have_tag('div')
47
43
  end
48
-
49
44
  end
50
45
  ```
51
46
 
@@ -55,7 +50,7 @@ Cucumber configuration:
55
50
  World RSpecHtmlMatchers
56
51
  ```
57
52
 
58
- as this gem requires **nokogiri**, here are [instructions for installing it](http://nokogiri.org/tutorials/installing_nokogiri.html).
53
+ as this gem requires **nokogiri**, here are [instructions for installing it](https://nokogiri.org/tutorials/installing_nokogiri.html).
59
54
 
60
55
  Usage
61
56
  -----
@@ -85,7 +80,7 @@ expect(rendered).to have_tag('form', :with => { :action => '/users', :method =>
85
80
  end
86
81
  ```
87
82
 
88
- Example above should be self-descriptive, if not, please refer to the [`have_tag`](http://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers%3Ahave_tag) documentation
83
+ Example above should be self-descriptive, if not, please refer to the [`have_tag`](https://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers%3Ahave_tag) documentation
89
84
 
90
85
  Input can be any html string. Let's take a look at these examples:
91
86
 
@@ -152,7 +147,7 @@ Input can be any html string. Let's take a look at these examples:
152
147
  expect('<p> Some content&nbsphere</p>').to have_tag('p', :text => mymock.text)
153
148
  # or
154
149
  expect('<p> Some content&nbsphere</p>').to have_tag('p') do
155
- with_content mymock.text
150
+ with_text mymock.text
156
151
  end
157
152
 
158
153
  # matching text content as it's seen by user:
@@ -193,7 +188,7 @@ where `page` is an instance of Capybara::Session
193
188
  - with\_date\_field
194
189
 
195
190
  and of course you can use the `without_` matchers,
196
- for more info take a look at [documentation](http://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers)
191
+ for more info take a look at [documentation](https://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers)
197
192
 
198
193
  ### rspec 1 partial backwards compatibility:
199
194
 
@@ -204,7 +199,7 @@ expect(response).to have_tag('div', 'expected content')
204
199
  expect(response).to have_tag('div', /regexp matching expected content/)
205
200
  ```
206
201
 
207
- [RSpec 1 `have_tag` documentation](http://old.rspec.info/rails/writing/views.html)
202
+ [RSpec 1 `have_tag` documentation](https://old.rspec.info/rails/writing/views.html)
208
203
 
209
204
  Matching Tag Attributes
210
205
  -----------------------
@@ -218,7 +213,7 @@ expect(index).to have_tag("img[alt!='']")
218
213
  More info
219
214
  ---------
220
215
 
221
- You can find more on [documentation](http://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers)
216
+ You can find more on [documentation](https://www.rubydoc.info/gems/rspec-html-matchers/RSpecHtmlMatchers)
222
217
 
223
218
  Also, please read [CHANGELOG](https://github.com/kucaahbe/rspec-html-matchers/blob/master/CHANGELOG.md) and [issues](https://github.com/kucaahbe/rspec-html-matchers/issues), might be helpful.
224
219
 
@@ -234,8 +229,8 @@ Contribution
234
229
  Contributors
235
230
  ============
236
231
 
237
- - [Kelly Felkins](http://github.com/kellyfelkins)
238
- - [Ryan Wilcox](http://github.com/rwilcox)
232
+ - [Kelly Felkins](https://github.com/kellyfelkins)
233
+ - [Ryan Wilcox](https://github.com/rwilcox)
239
234
  - [Simon Schoeters](https://github.com/cimm)
240
235
  - [Felix Tjandrawibawa](https://github.com/cemenghttps://github.com/cemeng)
241
236
  - [Szymon Przybył](https://github.com/apocalyptiq)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Given /^I have following template:$/ do |string|
2
- File.open($INDEX_HTML,'w+') do |file|
4
+ File.open($INDEX_HTML, 'w+') do |file| # rubocop:disable Style/GlobalVars
3
5
  file.write(string)
4
6
  end
5
7
  end
@@ -1,20 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # setup rspec matchers
2
4
  require 'rspec/expectations'
3
5
  World(RSpec::Matchers)
4
6
  require 'sinatra/base'
5
7
  require 'capybara/cucumber'
6
8
  require 'rspec-html-matchers'
9
+ require 'webdrivers'
10
+ require 'selenium-webdriver'
7
11
 
8
12
  World RSpecHtmlMatchers
9
13
 
10
- $ASSETS_DIR = File.expand_path('../tmp',__FILE__)
11
- $INDEX_HTML = File.join($ASSETS_DIR,'index.html')
14
+ # rubocop:disable Style/GlobalVars
15
+ $ASSETS_DIR = File.expand_path('../tmp', __FILE__)
16
+ $INDEX_HTML = File.join($ASSETS_DIR, 'index.html')
12
17
 
13
18
  class SimpleApp < Sinatra::Base
14
19
  set :public_folder, $ASSETS_DIR
15
20
  end
16
21
 
17
- Capybara.default_driver = :selenium
22
+ Capybara.register_driver :headless_chrome do |app|
23
+ options = Selenium::WebDriver::Chrome::Options.new
24
+ options.add_argument('--headless')
25
+ options.add_argument('--disable-gpu')
26
+ options.add_argument('--window-size=800,600')
27
+
28
+ Capybara::Selenium::Driver.new(app, :browser => :chrome, :options => options)
29
+ end
30
+
31
+ Capybara.configure do |config|
32
+ config.default_max_wait_time = 15 if config.respond_to? :default_max_wait_time=
33
+ config.default_driver = :headless_chrome
34
+ end
35
+
18
36
  Capybara.app = SimpleApp
19
37
 
20
38
  Before do
@@ -24,3 +42,4 @@ end
24
42
  After do
25
43
  FileUtils.rm_rf $ASSETS_DIR
26
44
  end
45
+ # rubocop:enable Style/GlobalVars
@@ -1,266 +1,30 @@
1
- # frozen_string_literal: true
2
1
  # encoding: UTF-8
3
- require 'rspec'
4
- require 'nokogiri'
2
+ # frozen_string_literal: true
5
3
 
4
+ require 'rspec-html-matchers/nokogiri_regexp_helper'
5
+ require 'rspec-html-matchers/nokogiri_text_helper'
6
+ require 'rspec-html-matchers/have_tag'
7
+
8
+ # RSpec global configuration:
9
+ #
10
+ # RSpec.configure do |config|
11
+ # config.include RSpecHtmlMatchers
12
+ # end
13
+ #
14
+ # RSpec per-test configuration
15
+ #
16
+ # RSpec.describe "my view spec" do
17
+ # include RSpecHtmlMatchers
18
+ #
19
+ # it "has tags" do
20
+ # expect(rendered).to have_tag('div')
21
+ # end
22
+ # end
23
+ #
24
+ # Cucumber configuration:
25
+ #
26
+ # World RSpecHtmlMatchers
6
27
  module RSpecHtmlMatchers
7
-
8
- # @api
9
- # @private
10
- # for nokogiri regexp matching
11
- class NokogiriRegexpHelper
12
- def initialize(regex)
13
- @regex = regex
14
- end
15
-
16
- def regexp node_set
17
- node_set.find_all { |node| node.content =~ @regex }
18
- end
19
- end
20
-
21
- # @api
22
- # @private
23
- class NokogiriTextHelper
24
- NON_BREAKING_SPACE = "\u00a0"
25
-
26
- def initialize text, squeeze_text = false
27
- @text = text
28
- @squeeze_text = squeeze_text
29
- end
30
-
31
- def content node_set
32
- node_set.find_all do |node|
33
- actual_content = node.content.gsub(NON_BREAKING_SPACE, ' ')
34
- actual_content = node.content.strip.squeeze(' ') if @squeeze_text
35
-
36
- actual_content == @text
37
- end
38
- end
39
- end
40
-
41
- # @api
42
- # @private
43
- class HaveTag
44
- attr_reader :failure_message, :failure_message_when_negated
45
- attr_reader :parent_scope, :current_scope
46
-
47
- DESCRIPTIONS = {
48
- :have_at_least_1 => %Q|have at least 1 element matching "%s"|,
49
- :have_n => %Q|have %i element(s) matching "%s"|
50
- }
51
-
52
- MESSAGES = {
53
- :expected_tag => %Q|expected following:\n%s\nto #{DESCRIPTIONS[:have_at_least_1]}, found 0.|,
54
- :unexpected_tag => %Q|expected following:\n%s\nto NOT have element matching "%s", found %s.|,
55
-
56
- :expected_count => %Q|expected following:\n%s\nto #{DESCRIPTIONS[:have_n]}, found %s.|,
57
- :unexpected_count => %Q|expected following:\n%s\nto NOT have %i element(s) matching "%s", but found.|,
58
-
59
- :expected_btw_count => %Q|expected following:\n%s\nto have at least %i and at most %i element(s) matching "%s", found %i.|,
60
- :unexpected_btw_count => %Q|expected following:\n%s\nto NOT have at least %i and at most %i element(s) matching "%s", but found %i.|,
61
-
62
- :expected_at_most => %Q|expected following:\n%s\nto have at most %i element(s) matching "%s", found %i.|,
63
- :unexpected_at_most => %Q|expected following:\n%s\nto NOT have at most %i element(s) matching "%s", but found %i.|,
64
-
65
- :expected_at_least => %Q|expected following:\n%s\nto have at least %i element(s) matching "%s", found %i.|,
66
- :unexpected_at_least => %Q|expected following:\n%s\nto NOT have at least %i element(s) matching "%s", but found %i.|,
67
-
68
- :expected_regexp => %Q|%s regexp expected within "%s" in following template:\n%s|,
69
- :unexpected_regexp => %Q|%s regexp unexpected within "%s" in following template:\n%s\nbut was found.|,
70
-
71
- :expected_text => %Q|"%s" expected within "%s" in following template:\n%s|,
72
- :unexpected_text => %Q|"%s" unexpected within "%s" in following template:\n%s\nbut was found.|,
73
-
74
- :wrong_count_error => %Q|:count with :minimum or :maximum has no sence!|,
75
- :min_max_error => %Q|:minimum should be less than :maximum!|,
76
- :bad_range_error => %Q|Your :count range(%s) has no sence!|,
77
- }
78
-
79
-
80
- def initialize tag, options={}, &block
81
- @tag, @options, @block = tag.to_s, options, block
82
-
83
- if with_attrs = @options.delete(:with)
84
- if classes = with_attrs.delete(:class)
85
- @tag += '.' + classes_to_selector(classes)
86
- end
87
- selector = with_attrs.inject('') do |html_attrs_string, (k, v)|
88
- html_attrs_string += "[#{k}='#{v}']"
89
- html_attrs_string
90
- end
91
- @tag += selector
92
- end
93
-
94
- if without_attrs = @options.delete(:without)
95
- if classes = without_attrs.delete(:class)
96
- @tag += ":not(.#{classes_to_selector(classes)})"
97
- end
98
- end
99
-
100
- validate_options!
101
- set_options
102
- end
103
-
104
- def matches? document, &block
105
- @block = block if block
106
-
107
- document = document.html if defined?(Capybara::Session) && document.is_a?(Capybara::Session)
108
-
109
- case document
110
- when String
111
- @parent_scope = @current_scope = Nokogiri::HTML(document).css(@tag)
112
- @document = document
113
- else
114
- @parent_scope = document.current_scope
115
- @current_scope = begin
116
- document.parent_scope.css(@tag)
117
- # on jruby this produce exception if css was not found:
118
- # undefined method `decorate' for nil:NilClass
119
- rescue NoMethodError
120
- Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new)
121
- end
122
- @document = @parent_scope.to_html
123
- end
124
-
125
- if tag_presents? and text_right? and count_right?
126
- @current_scope = @parent_scope
127
- @block.call if @block
128
- true
129
- else
130
- false
131
- end
132
- end
133
-
134
- def description
135
- # TODO should it be more complicated?
136
- if @options.has_key?(:count)
137
- DESCRIPTIONS[:have_n] % [@options[:count],@tag]
138
- else
139
- DESCRIPTIONS[:have_at_least_1] % @tag
140
- end
141
- end
142
-
143
- private
144
-
145
- def classes_to_selector(classes)
146
- case classes
147
- when Array
148
- classes.join('.')
149
- when String
150
- classes.gsub(/\s+/, '.')
151
- end
152
- end
153
-
154
- def tag_presents?
155
- if @current_scope.first
156
- @count = @current_scope.count
157
- @failure_message_when_negated = MESSAGES[:unexpected_tag] % [@document, @tag, @count]
158
- true
159
- else
160
- @failure_message = MESSAGES[:expected_tag] % [@document, @tag]
161
- false
162
- end
163
- end
164
-
165
- def count_right?
166
- case @options[:count]
167
- when Integer
168
- ((@failure_message_when_negated=MESSAGES[:unexpected_count] % [@document,@count,@tag]) && @count == @options[:count]) || (@failure_message=MESSAGES[:expected_count] % [@document,@options[:count],@tag,@count]; false)
169
- when Range
170
- ((@failure_message_when_negated=MESSAGES[:unexpected_btw_count] % [@document,@options[:count].min,@options[:count].max,@tag,@count]) && @options[:count].member?(@count)) || (@failure_message=MESSAGES[:expected_btw_count] % [@document,@options[:count].min,@options[:count].max,@tag,@count]; false)
171
- when nil
172
- if @options[:maximum]
173
- ((@failure_message_when_negated=MESSAGES[:unexpected_at_most] % [@document,@options[:maximum],@tag,@count]) && @count <= @options[:maximum]) || (@failure_message=MESSAGES[:expected_at_most] % [@document,@options[:maximum],@tag,@count]; false)
174
- elsif @options[:minimum]
175
- ((@failure_message_when_negated=MESSAGES[:unexpected_at_least] % [@document,@options[:minimum],@tag,@count]) && @count >= @options[:minimum]) || (@failure_message=MESSAGES[:expected_at_least] % [@document,@options[:minimum],@tag,@count]; false)
176
- else
177
- true
178
- end
179
- end
180
- end
181
-
182
- def text_right?
183
- return true unless @options[:text]
184
-
185
- case text=@options[:text]
186
- when Regexp
187
- new_scope = @current_scope.css(':regexp()',NokogiriRegexpHelper.new(text))
188
- unless new_scope.empty?
189
- @count = new_scope.count
190
- @failure_message_when_negated = MESSAGES[:unexpected_regexp] % [text.inspect,@tag,@document]
191
- true
192
- else
193
- @failure_message = MESSAGES[:expected_regexp] % [text.inspect,@tag,@document]
194
- false
195
- end
196
- else
197
- new_scope = @current_scope.css(':content()', NokogiriTextHelper.new(text, @options[:squeeze_text]))
198
- unless new_scope.empty?
199
- @count = new_scope.count
200
- @failure_message_when_negated = MESSAGES[:unexpected_text] % [text,@tag,@document]
201
- true
202
- else
203
- @failure_message = MESSAGES[:expected_text] % [text,@tag,@document]
204
- false
205
- end
206
- end
207
- end
208
-
209
- protected
210
-
211
- def validate_options!
212
- validate_count_presence!
213
- validate_count_when_set_min_max!
214
- validate_count_when_set_range!
215
- end
216
-
217
- def validate_count_presence!
218
- raise 'wrong :count specified' unless [Range, NilClass].include?(@options[:count].class) or @options[:count].is_a?(Integer)
219
-
220
- [:min, :minimum, :max, :maximum].each do |key|
221
- raise MESSAGES[:wrong_count_error] if @options.has_key?(key) and @options.has_key?(:count)
222
- end
223
- end
224
-
225
- def validate_count_when_set_min_max!
226
- begin
227
- raise MESSAGES[:min_max_error] if @options[:minimum] > @options[:maximum]
228
- rescue NoMethodError # nil > 4
229
- rescue ArgumentError # 2 < nil
230
- end
231
- end
232
-
233
- def validate_count_when_set_range!
234
- begin
235
- begin
236
- raise MESSAGES[:bad_range_error] % [@options[:count].to_s] if count_is_range_but_no_min?
237
- rescue ArgumentError, "comparison of String with" # if @options[:count] == 'a'..'z'
238
- raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
239
- end
240
- rescue TypeError # fix for 1.8.7 for 'rescue ArgumentError, "comparison of String with"' stroke
241
- raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
242
- end
243
- end
244
-
245
- def count_is_range_but_no_min?
246
- @options[:count] && @options[:count].is_a?(Range) &&
247
- (@options[:count].min.nil? or @options[:count].min < 0)
248
- end
249
-
250
- def set_options
251
- @options[:minimum] ||= @options.delete(:min)
252
- @options[:maximum] ||= @options.delete(:max)
253
-
254
- @options[:text] = @options[:text].to_s if @options.has_key?(:text) && !@options[:text].is_a?(Regexp)
255
-
256
- if @options.has_key?(:seen) && !@options[:seen].is_a?(Regexp)
257
- @options[:text] = @options[:seen].to_s
258
- @options[:squeeze_text] = true
259
- end
260
- end
261
-
262
- end
263
-
264
28
  # tag assertion, this is the core of functionality, other matchers are shortcuts to this matcher
265
29
  #
266
30
  # @yield block where you should put with_tag, without_tag and/or other matchers
@@ -295,45 +59,60 @@ module RSpecHtmlMatchers
295
59
  # </html>").to have_tag('body') { with_tag('h1', :text => 'some html document') }
296
60
  # expect('<div class="one two">').to have_tag('div', :with => { :class => ['two', 'one'] })
297
61
  # expect('<div class="one two">').to have_tag('div', :with => { :class => 'two one' })
298
- def have_tag tag, options={}, &block
62
+ def have_tag tag, options = {}, &block
299
63
  # for backwards compatibility with rpecs have tag:
300
- options = { :text => options } if options.kind_of?(String) || options.kind_of?(Regexp)
64
+ options = { :text => options } if options.is_a?(String) || options.is_a?(Regexp)
301
65
  @__current_scope_for_nokogiri_matcher = HaveTag.new(tag, options, &block)
302
66
  end
303
67
 
304
- def have_empty_tag tag, options={}
305
- have_tag(tag, options.merge(text: ""))
68
+ # tests whether tag have any content inside
69
+ #
70
+ # @example
71
+ # expect('<div></div>').to have_empty_tag('div') # success
72
+ # expect('<div>hi</div>').to have_empty_tag('div') # fail
73
+ def have_empty_tag tag, options = {}
74
+ have_tag(tag, options.merge(:blank => true))
306
75
  end
307
76
 
308
77
  def with_text text
309
78
  raise StandardError, 'this matcher should be used inside "have_tag" matcher block' unless defined?(@__current_scope_for_nokogiri_matcher)
310
79
  raise ArgumentError, 'this matcher does not accept block' if block_given?
80
+
311
81
  tag = @__current_scope_for_nokogiri_matcher.instance_variable_get(:@tag)
312
- expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, :text => text)
82
+ within_nested_tag do
83
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, :text => text)
84
+ end
313
85
  end
314
86
 
315
87
  def without_text text
316
88
  raise StandardError, 'this matcher should be used inside "have_tag" matcher block' unless defined?(@__current_scope_for_nokogiri_matcher)
317
89
  raise ArgumentError, 'this matcher does not accept block' if block_given?
90
+
318
91
  tag = @__current_scope_for_nokogiri_matcher.instance_variable_get(:@tag)
319
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, :text => text)
92
+ within_nested_tag do
93
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, :text => text)
94
+ end
320
95
  end
321
- alias :but_without_text :without_text
96
+ alias but_without_text without_text
322
97
 
323
98
  # with_tag matcher
324
99
  # @yield block where you should put other with_tag or without_tag
325
100
  # @see #have_tag
326
101
  # @note this should be used within block of have_tag matcher
327
- def with_tag tag, options={}, &block
328
- expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options, &block)
102
+ def with_tag tag, options = {}, &block
103
+ within_nested_tag do
104
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options, &block)
105
+ end
329
106
  end
330
107
 
331
108
  # without_tag matcher
332
109
  # @yield block where you should put other with_tag or without_tag
333
110
  # @see #have_tag
334
111
  # @note this should be used within block of have_tag matcher
335
- def without_tag tag, options={}, &block
336
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options, &block)
112
+ def without_tag tag, options = {}, &block
113
+ within_nested_tag do
114
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options, &block)
115
+ end
337
116
  end
338
117
 
339
118
  # form assertion
@@ -342,250 +121,275 @@ module RSpecHtmlMatchers
342
121
  # have_tag 'form', :with => { :action => action_url, :method => method ... }
343
122
  # @yield block with with_<field>, see below
344
123
  # @see #have_tag
345
- def have_form action_url, method, options={}, &block
124
+ def have_form action_url, method, options = {}, &block
346
125
  options[:with] ||= {}
347
126
  id = options[:with].delete(:id)
348
- tag = 'form'; tag += '#'+id if id
127
+ tag = 'form'; tag += '#' + id if id
349
128
  options[:with].merge!(:action => action_url)
350
129
  options[:with].merge!(:method => method.to_s)
351
130
  have_tag tag, options, &block
352
131
  end
353
132
 
354
- #@TODO fix code duplications
133
+ # @TODO fix code duplications
355
134
 
356
- def with_hidden_field name, value=nil
357
- options = form_tag_options('hidden',name,value)
135
+ def with_hidden_field name, value = nil
136
+ options = form_tag_options('hidden', name, value)
358
137
  should_have_input(options)
359
138
  end
360
139
 
361
- def without_hidden_field name, value=nil
362
- options = form_tag_options('hidden',name,value)
140
+ def without_hidden_field name, value = nil
141
+ options = form_tag_options('hidden', name, value)
363
142
  should_not_have_input(options)
364
143
  end
365
144
 
366
- def with_text_field name, value=nil
367
- options = form_tag_options('text',name,value)
145
+ def with_text_field name, value = nil
146
+ options = form_tag_options('text', name, value)
368
147
  should_have_input(options)
369
148
  end
370
149
 
371
- def without_text_field name, value=nil
372
- options = form_tag_options('text',name,value)
150
+ def without_text_field name, value = nil
151
+ options = form_tag_options('text', name, value)
373
152
  should_not_have_input(options)
374
153
  end
375
154
 
376
- def with_email_field name, value=nil
377
- options = form_tag_options('email',name,value)
155
+ def with_email_field name, value = nil
156
+ options = form_tag_options('email', name, value)
378
157
  should_have_input(options)
379
158
  end
380
159
 
381
- def without_email_field name, value=nil
382
- options = form_tag_options('email',name,value)
160
+ def without_email_field name, value = nil
161
+ options = form_tag_options('email', name, value)
383
162
  should_not_have_input(options)
384
163
  end
385
164
 
386
- def with_url_field name, value=nil
387
- options = form_tag_options('url',name,value)
165
+ def with_url_field name, value = nil
166
+ options = form_tag_options('url', name, value)
388
167
  should_have_input(options)
389
168
  end
390
169
 
391
- def without_url_field name, value=nil
392
- options = form_tag_options('url',name,value)
170
+ def without_url_field name, value = nil
171
+ options = form_tag_options('url', name, value)
393
172
  should_not_have_input(options)
394
173
  end
395
174
 
396
- def with_number_field name, value=nil
397
- options = form_tag_options('number',name,value)
175
+ def with_number_field name, value = nil
176
+ options = form_tag_options('number', name, value)
398
177
  should_have_input(options)
399
178
  end
400
179
 
401
- def without_number_field name, value=nil
402
- options = form_tag_options('number',name,value)
180
+ def without_number_field name, value = nil
181
+ options = form_tag_options('number', name, value)
403
182
  should_not_have_input(options)
404
183
  end
405
184
 
406
- def with_range_field name, min, max, options={}
407
- options = { :with => { :name => name, :type => 'range', :min => min.to_s, :max => max.to_s }.merge(options.delete(:with)||{}) }
185
+ def with_range_field name, min, max, options = {}
186
+ options = { :with => { :name => name, :type => 'range', :min => min.to_s, :max => max.to_s }.merge(options.delete(:with) || {}) }
408
187
  should_have_input(options)
409
188
  end
410
189
 
411
- def without_range_field name, min=nil, max=nil, options={}
412
- options = { :with => { :name => name, :type => 'range' }.merge(options.delete(:with)||{}) }
190
+ def without_range_field name, min = nil, max = nil, options = {}
191
+ options = { :with => { :name => name, :type => 'range' }.merge(options.delete(:with) || {}) }
413
192
  options[:with].merge!(:min => min.to_s) if min
414
193
  options[:with].merge!(:max => max.to_s) if max
415
194
  should_not_have_input(options)
416
195
  end
417
196
 
418
- DATE_FIELD_TYPES = %w( date month week time datetime datetime-local )
197
+ DATE_FIELD_TYPES = %w[date month week time datetime datetime-local].freeze
419
198
 
420
- def with_date_field date_field_type, name=nil, options={}
199
+ def with_date_field date_field_type, name = nil, options = {}
421
200
  date_field_type = date_field_type.to_s
422
201
  raise "unknown type `#{date_field_type}` for date picker" unless DATE_FIELD_TYPES.include?(date_field_type)
423
- options = { :with => { :type => date_field_type.to_s }.merge(options.delete(:with)||{}) }
202
+
203
+ options = { :with => { :type => date_field_type.to_s }.merge(options.delete(:with) || {}) }
424
204
  options[:with].merge!(:name => name.to_s) if name
425
205
  should_have_input(options)
426
206
  end
427
207
 
428
- def without_date_field date_field_type, name=nil, options={}
208
+ def without_date_field date_field_type, name = nil, options = {}
429
209
  date_field_type = date_field_type.to_s
430
210
  raise "unknown type `#{date_field_type}` for date picker" unless DATE_FIELD_TYPES.include?(date_field_type)
431
- options = { :with => { :type => date_field_type.to_s }.merge(options.delete(:with)||{}) }
211
+
212
+ options = { :with => { :type => date_field_type.to_s }.merge(options.delete(:with) || {}) }
432
213
  options[:with].merge!(:name => name.to_s) if name
433
214
  should_not_have_input(options)
434
215
  end
435
216
 
436
- def with_password_field name, value=nil
437
- # TODO add ability to explicitly say that value should be empty
438
- options = form_tag_options('password',name,value)
217
+ def with_password_field name, value = nil
218
+ # TODO: add ability to explicitly say that value should be empty
219
+ options = form_tag_options('password', name, value)
439
220
  should_have_input(options)
440
221
  end
441
222
 
442
- def without_password_field name, value=nil
443
- options = form_tag_options('password',name,value)
223
+ def without_password_field name, value = nil
224
+ options = form_tag_options('password', name, value)
444
225
  should_not_have_input(options)
445
226
  end
446
227
 
447
- def with_file_field name, value=nil
448
- options = form_tag_options('file',name,value)
228
+ def with_file_field name, value = nil
229
+ options = form_tag_options('file', name, value)
449
230
  should_have_input(options)
450
231
  end
451
232
 
452
- def without_file_field name, value=nil
453
- options = form_tag_options('file',name,value)
233
+ def without_file_field name, value = nil
234
+ options = form_tag_options('file', name, value)
454
235
  should_not_have_input(options)
455
236
  end
456
237
 
457
238
  def with_text_area name
458
239
  # TODO, should be: with_text_area name, text=nil
459
- #options = form_tag_options('text',name,value)
240
+ # options = form_tag_options('text',name,value)
460
241
  options = { :with => { :name => name } }
461
- expect(@__current_scope_for_nokogiri_matcher).to have_tag('textarea', options)
242
+ within_nested_tag do
243
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag('textarea', options)
244
+ end
462
245
  end
463
246
 
464
247
  def without_text_area name
465
248
  # TODO, should be: without_text_area name, text=nil
466
- #options = form_tag_options('text',name,value)
249
+ # options = form_tag_options('text',name,value)
467
250
  options = { :with => { :name => name } }
468
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('textarea', options)
251
+ within_nested_tag do
252
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('textarea', options)
253
+ end
469
254
  end
470
255
 
471
- def with_checkbox name, value=nil
472
- options = form_tag_options('checkbox',name,value)
256
+ def with_checkbox name, value = nil
257
+ options = form_tag_options('checkbox', name, value)
473
258
  should_have_input(options)
474
259
  end
475
260
 
476
- def without_checkbox name, value=nil
477
- options = form_tag_options('checkbox',name,value)
261
+ def without_checkbox name, value = nil
262
+ options = form_tag_options('checkbox', name, value)
478
263
  should_not_have_input(options)
479
264
  end
480
265
 
481
266
  def with_radio_button name, value
482
- options = form_tag_options('radio',name,value)
267
+ options = form_tag_options('radio', name, value)
483
268
  should_have_input(options)
484
269
  end
485
270
 
486
271
  def without_radio_button name, value
487
- options = form_tag_options('radio',name,value)
272
+ options = form_tag_options('radio', name, value)
488
273
  should_not_have_input(options)
489
274
  end
490
275
 
491
- def with_select name, options={}, &block
276
+ def with_select name, options = {}, &block
492
277
  options[:with] ||= {}
493
278
  id = options[:with].delete(:id)
494
- tag='select'; tag += '#'+id if id
279
+ tag = 'select'; tag += '#' + id if id
495
280
  options[:with].merge!(:name => name)
496
- expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options, &block)
281
+ within_nested_tag do
282
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options, &block)
283
+ end
497
284
  end
498
285
 
499
- def without_select name, options={}, &block
286
+ def without_select name, options = {}, &block
500
287
  options[:with] ||= {}
501
288
  id = options[:with].delete(:id)
502
- tag='select'; tag += '#'+id if id
289
+ tag = 'select'; tag += '#' + id if id
503
290
  options[:with].merge!(:name => name)
504
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options, &block)
291
+ within_nested_tag do
292
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options, &block)
293
+ end
505
294
  end
506
295
 
507
- def with_option text, value=nil, options={}
296
+ def with_option text, value = nil, options = {}
508
297
  options[:with] ||= {}
509
298
  if value.is_a?(Hash)
510
299
  options.merge!(value)
511
- value=nil
300
+ value = nil
512
301
  end
513
- tag='option'
302
+ tag = 'option'
514
303
  options[:with].merge!(:value => value.to_s) if value
515
- if options[:selected]
516
- options[:with].merge!(:selected => "selected")
517
- end
304
+ options[:with].merge!(:selected => 'selected') if options[:selected]
518
305
  options.delete(:selected)
519
306
  options.merge!(:text => text) if text
520
- expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options)
307
+ within_nested_tag do
308
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag(tag, options)
309
+ end
521
310
  end
522
311
 
523
- def without_option text, value=nil, options={}
312
+ def without_option text, value = nil, options = {}
524
313
  options[:with] ||= {}
525
314
  if value.is_a?(Hash)
526
315
  options.merge!(value)
527
- value=nil
316
+ value = nil
528
317
  end
529
- tag='option'
318
+ tag = 'option'
530
319
  options[:with].merge!(:value => value.to_s) if value
531
- if options[:selected]
532
- options[:with].merge!(:selected => "selected")
533
- end
320
+ options[:with].merge!(:selected => 'selected') if options[:selected]
534
321
  options.delete(:selected)
535
322
  options.merge!(:text => text) if text
536
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options)
323
+ within_nested_tag do
324
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag(tag, options)
325
+ end
537
326
  end
538
327
 
539
- def with_button text, value=nil, options={}
328
+ def with_button text, value = nil, options = {}
540
329
  options[:with] ||= {}
541
330
  if value.is_a?(Hash)
542
331
  options.merge!(value)
543
- value=nil
332
+ value = nil
544
333
  end
545
334
  options[:with].merge!(:value => value.to_s) if value
546
335
  options.merge!(:text => text) if text
547
- expect(@__current_scope_for_nokogiri_matcher).to have_tag('button', options)
336
+ within_nested_tag do
337
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag('button', options)
338
+ end
548
339
  end
549
340
 
550
- def without_button text, value=nil, options={}
341
+ def without_button text, value = nil, options = {}
551
342
  options[:with] ||= {}
552
343
  if value.is_a?(Hash)
553
344
  options.merge!(value)
554
- value=nil
345
+ value = nil
555
346
  end
556
347
  options[:with].merge!(:value => value.to_s) if value
557
348
  options.merge!(:text => text) if text
558
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('button', options)
349
+ within_nested_tag do
350
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('button', options)
351
+ end
559
352
  end
560
353
 
561
354
  def with_submit value
562
355
  options = { :with => { :type => 'submit', :value => value } }
563
- #options = form_tag_options('text',name,value)
356
+ # options = form_tag_options('text',name,value)
564
357
  should_have_input(options)
565
358
  end
566
359
 
567
360
  def without_submit value
568
- #options = form_tag_options('text',name,value)
361
+ # options = form_tag_options('text',name,value)
569
362
  options = { :with => { :type => 'submit', :value => value } }
570
363
  should_not_have_input(options)
571
364
  end
572
365
 
573
366
  private
574
367
 
575
- def should_have_input(options)
576
- expect(@__current_scope_for_nokogiri_matcher).to have_tag('input', options)
368
+ def should_have_input options
369
+ within_nested_tag do
370
+ expect(@__current_scope_for_nokogiri_matcher).to have_tag('input', options)
371
+ end
577
372
  end
578
373
 
579
- def should_not_have_input(options)
580
- expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('input', options)
374
+ def should_not_have_input options
375
+ within_nested_tag do
376
+ expect(@__current_scope_for_nokogiri_matcher).to_not have_tag('input', options)
377
+ end
581
378
  end
582
379
 
583
380
  # form_tag in method name name mean smth. like input, submit, tags that should appear in a form
584
- def form_tag_options form_tag_type, form_tag_name, form_tag_value=nil
381
+ def form_tag_options form_tag_type, form_tag_name, form_tag_value = nil
585
382
  options = { :with => { :name => form_tag_name, :type => form_tag_type } }
586
383
  # .to_s if value is a digit or smth. else, see issue#10
587
384
  options[:with].merge!(:value => form_tag_value.to_s) if form_tag_value
588
- return options
385
+ options
589
386
  end
590
387
 
388
+ def within_nested_tag &block
389
+ raise 'block needed' unless block_given?
390
+
391
+ parent_scope = @__current_scope_for_nokogiri_matcher
392
+ block.call
393
+ @__current_scope_for_nokogiri_matcher = parent_scope
394
+ end
591
395
  end