rspec-html-matchers 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -2
- data/README.md +1 -1
- data/lib/rspec-html-matchers.rb +6 -259
- data/lib/rspec-html-matchers/have_tag.rb +231 -0
- data/lib/rspec-html-matchers/nokogiri_regexp_helper.rb +19 -0
- data/lib/rspec-html-matchers/nokogiri_text_helper.rb +26 -0
- data/lib/rspec-html-matchers/version.rb +5 -0
- data/spec/form_matchers_spec.rb +13 -13
- data/spec/have_empty_tag_spec.rb +4 -4
- data/spec/have_tag_spec.rb +15 -12
- data/spec/spec_helper.rb +1 -1
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cc4b6854824aeacab03d525dc5a1b63cb1beb3d
|
4
|
+
data.tar.gz: c5d1a519855e7188533cf3967147d835fd4fd818
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8216b50ce200d8cddb1071016529de18c311c2ad8ae34a99b3e7bda47d8560518fe58a10d78baaed350d4fc01a17a4dad05facb8bb914e16f46f56549b8d2c2c
|
7
|
+
data.tar.gz: d792ede080051791c628792831e345f3a00adfe1eeaf0fd94554e7c9b123454460672a00d63159a84289cdc77571d60d7f2901585d8be53f60a9e7bab35be680
|
data/CHANGELOG.md
CHANGED
@@ -6,15 +6,21 @@ unreleased(TODO)
|
|
6
6
|
|
7
7
|
* with_tag should raise error when used outside have_tag
|
8
8
|
* add ability to have_form('/url', 'PUT') or have_form('/url', :PUT)
|
9
|
-
*
|
9
|
+
* intelligent check comments(make sure it is not searching inside comments)
|
10
10
|
* shouldn't show all markup in error message if it is too big
|
11
11
|
* order matching
|
12
12
|
* improve documentation, add more usage examples (look at changelog and code!)
|
13
13
|
|
14
|
+
0.9.1
|
15
|
+
-----
|
16
|
+
|
17
|
+
* re-added ruby support back to 1.8.7 (supporting same versions as rspec 3)
|
18
|
+
* split lib in few files
|
19
|
+
|
14
20
|
0.9.0
|
15
21
|
-----
|
16
22
|
|
17
|
-
* fixed `
|
23
|
+
* fixed `with_tag` nesting (thanks to [randoum](https://github.com/randoum): [#59](https://github.com/kucaahbe/rspec-html-matchers/pull/59))
|
18
24
|
* added ~> 2.4 ruby support
|
19
25
|
* removed ~> 2 ruby support
|
20
26
|
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ rspec-html-matchers
|
|
9
9
|
Goals
|
10
10
|
-----
|
11
11
|
|
12
|
-
* for testing **
|
12
|
+
* designed for testing **complex** html output. If you plan to perform simple matching, consider using:
|
13
13
|
* [assert_select](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/SelectorAssertions.html#method-i-assert_select)
|
14
14
|
* [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
15
|
* [matchers provided by capybara](http://rdoc.info/github/jnicklas/capybara/Capybara/Node/Matchers)
|
data/lib/rspec-html-matchers.rb
CHANGED
@@ -1,267 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# encoding: UTF-8
|
3
|
+
|
3
4
|
require 'rspec'
|
4
5
|
require 'nokogiri'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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 = Nokogiri::HTML(document)
|
112
|
-
@document = document
|
113
|
-
else
|
114
|
-
@parent_scope = document.current_scope
|
115
|
-
@document = @parent_scope.to_html
|
116
|
-
end
|
117
|
-
@current_scope = begin
|
118
|
-
@parent_scope.css(@tag)
|
119
|
-
# on jruby this produce exception if css was not found:
|
120
|
-
# undefined method `decorate' for nil:NilClass
|
121
|
-
rescue NoMethodError
|
122
|
-
Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new)
|
123
|
-
end
|
124
|
-
if tag_presents? and text_right? and count_right?
|
125
|
-
@block.call(self) if @block
|
126
|
-
true
|
127
|
-
else
|
128
|
-
false
|
129
|
-
end
|
130
|
-
end
|
7
|
+
require 'rspec-html-matchers/nokogiri_regexp_helper'
|
8
|
+
require 'rspec-html-matchers/nokogiri_text_helper'
|
9
|
+
require 'rspec-html-matchers/have_tag'
|
131
10
|
|
132
|
-
|
133
|
-
@document
|
134
|
-
end
|
135
|
-
|
136
|
-
def description
|
137
|
-
# TODO should it be more complicated?
|
138
|
-
if @options.has_key?(:count)
|
139
|
-
DESCRIPTIONS[:have_n] % [@options[:count],@tag]
|
140
|
-
else
|
141
|
-
DESCRIPTIONS[:have_at_least_1] % @tag
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
def classes_to_selector(classes)
|
148
|
-
case classes
|
149
|
-
when Array
|
150
|
-
classes.join('.')
|
151
|
-
when String
|
152
|
-
classes.gsub(/\s+/, '.')
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def tag_presents?
|
157
|
-
if @current_scope.first
|
158
|
-
@count = @current_scope.count
|
159
|
-
@failure_message_when_negated = MESSAGES[:unexpected_tag] % [@document, @tag, @count]
|
160
|
-
true
|
161
|
-
else
|
162
|
-
@failure_message = MESSAGES[:expected_tag] % [@document, @tag]
|
163
|
-
false
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
def count_right?
|
168
|
-
case @options[:count]
|
169
|
-
when Integer
|
170
|
-
((@failure_message_when_negated=MESSAGES[:unexpected_count] % [@document,@count,@tag]) && @count == @options[:count]) || (@failure_message=MESSAGES[:expected_count] % [@document,@options[:count],@tag,@count]; false)
|
171
|
-
when Range
|
172
|
-
((@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)
|
173
|
-
when nil
|
174
|
-
if @options[:maximum]
|
175
|
-
((@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)
|
176
|
-
elsif @options[:minimum]
|
177
|
-
((@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)
|
178
|
-
else
|
179
|
-
true
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def text_right?
|
185
|
-
return true unless @options[:text]
|
186
|
-
|
187
|
-
case text=@options[:text]
|
188
|
-
when Regexp
|
189
|
-
new_scope = @current_scope.css(':regexp()',NokogiriRegexpHelper.new(text))
|
190
|
-
unless new_scope.empty?
|
191
|
-
@count = new_scope.count
|
192
|
-
@failure_message_when_negated = MESSAGES[:unexpected_regexp] % [text.inspect,@tag,@document]
|
193
|
-
true
|
194
|
-
else
|
195
|
-
@failure_message = MESSAGES[:expected_regexp] % [text.inspect,@tag,@document]
|
196
|
-
false
|
197
|
-
end
|
198
|
-
else
|
199
|
-
new_scope = @current_scope.css(':content()', NokogiriTextHelper.new(text, @options[:squeeze_text]))
|
200
|
-
unless new_scope.empty?
|
201
|
-
@count = new_scope.count
|
202
|
-
@failure_message_when_negated = MESSAGES[:unexpected_text] % [text,@tag,@document]
|
203
|
-
true
|
204
|
-
else
|
205
|
-
@failure_message = MESSAGES[:expected_text] % [text,@tag,@document]
|
206
|
-
false
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
protected
|
212
|
-
|
213
|
-
def validate_options!
|
214
|
-
validate_count_presence!
|
215
|
-
validate_count_when_set_min_max!
|
216
|
-
validate_count_when_set_range!
|
217
|
-
end
|
218
|
-
|
219
|
-
def validate_count_presence!
|
220
|
-
raise 'wrong :count specified' unless [Range, NilClass].include?(@options[:count].class) or @options[:count].is_a?(Integer)
|
221
|
-
|
222
|
-
[:min, :minimum, :max, :maximum].each do |key|
|
223
|
-
raise MESSAGES[:wrong_count_error] if @options.has_key?(key) and @options.has_key?(:count)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
def validate_count_when_set_min_max!
|
228
|
-
begin
|
229
|
-
raise MESSAGES[:min_max_error] if @options[:minimum] > @options[:maximum]
|
230
|
-
rescue NoMethodError # nil > 4
|
231
|
-
rescue ArgumentError # 2 < nil
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
def validate_count_when_set_range!
|
236
|
-
begin
|
237
|
-
begin
|
238
|
-
raise MESSAGES[:bad_range_error] % [@options[:count].to_s] if count_is_range_but_no_min?
|
239
|
-
rescue ArgumentError, "comparison of String with" # if @options[:count] == 'a'..'z'
|
240
|
-
raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
|
241
|
-
end
|
242
|
-
rescue TypeError # fix for 1.8.7 for 'rescue ArgumentError, "comparison of String with"' stroke
|
243
|
-
raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def count_is_range_but_no_min?
|
248
|
-
@options[:count] && @options[:count].is_a?(Range) &&
|
249
|
-
(@options[:count].min.nil? or @options[:count].min < 0)
|
250
|
-
end
|
251
|
-
|
252
|
-
def set_options
|
253
|
-
@options[:minimum] ||= @options.delete(:min)
|
254
|
-
@options[:maximum] ||= @options.delete(:max)
|
255
|
-
|
256
|
-
@options[:text] = @options[:text].to_s if @options.has_key?(:text) && !@options[:text].is_a?(Regexp)
|
257
|
-
|
258
|
-
if @options.has_key?(:seen) && !@options[:seen].is_a?(Regexp)
|
259
|
-
@options[:text] = @options[:seen].to_s
|
260
|
-
@options[:squeeze_text] = true
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
end
|
11
|
+
module RSpecHtmlMatchers
|
265
12
|
|
266
13
|
# tag assertion, this is the core of functionality, other matchers are shortcuts to this matcher
|
267
14
|
#
|
@@ -304,7 +51,7 @@ module RSpecHtmlMatchers
|
|
304
51
|
end
|
305
52
|
|
306
53
|
def have_empty_tag tag, options={}
|
307
|
-
have_tag(tag, options.merge(text
|
54
|
+
have_tag(tag, options.merge(:text => ""))
|
308
55
|
end
|
309
56
|
|
310
57
|
def with_text text
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
module RSpecHtmlMatchers
|
5
|
+
|
6
|
+
# @api
|
7
|
+
# @private
|
8
|
+
class HaveTag
|
9
|
+
attr_reader :failure_message, :failure_message_when_negated
|
10
|
+
attr_reader :parent_scope, :current_scope
|
11
|
+
|
12
|
+
DESCRIPTIONS = {
|
13
|
+
:have_at_least_1 => %Q|have at least 1 element matching "%s"|,
|
14
|
+
:have_n => %Q|have %i element(s) matching "%s"|
|
15
|
+
}
|
16
|
+
|
17
|
+
MESSAGES = {
|
18
|
+
:expected_tag => %Q|expected following:\n%s\nto #{DESCRIPTIONS[:have_at_least_1]}, found 0.|,
|
19
|
+
:unexpected_tag => %Q|expected following:\n%s\nto NOT have element matching "%s", found %s.|,
|
20
|
+
|
21
|
+
:expected_count => %Q|expected following:\n%s\nto #{DESCRIPTIONS[:have_n]}, found %s.|,
|
22
|
+
:unexpected_count => %Q|expected following:\n%s\nto NOT have %i element(s) matching "%s", but found.|,
|
23
|
+
|
24
|
+
:expected_btw_count => %Q|expected following:\n%s\nto have at least %i and at most %i element(s) matching "%s", found %i.|,
|
25
|
+
: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.|,
|
26
|
+
|
27
|
+
:expected_at_most => %Q|expected following:\n%s\nto have at most %i element(s) matching "%s", found %i.|,
|
28
|
+
:unexpected_at_most => %Q|expected following:\n%s\nto NOT have at most %i element(s) matching "%s", but found %i.|,
|
29
|
+
|
30
|
+
:expected_at_least => %Q|expected following:\n%s\nto have at least %i element(s) matching "%s", found %i.|,
|
31
|
+
:unexpected_at_least => %Q|expected following:\n%s\nto NOT have at least %i element(s) matching "%s", but found %i.|,
|
32
|
+
|
33
|
+
:expected_regexp => %Q|%s regexp expected within "%s" in following template:\n%s|,
|
34
|
+
:unexpected_regexp => %Q|%s regexp unexpected within "%s" in following template:\n%s\nbut was found.|,
|
35
|
+
|
36
|
+
:expected_text => %Q|"%s" expected within "%s" in following template:\n%s|,
|
37
|
+
:unexpected_text => %Q|"%s" unexpected within "%s" in following template:\n%s\nbut was found.|,
|
38
|
+
|
39
|
+
:wrong_count_error => %Q|:count with :minimum or :maximum has no sence!|,
|
40
|
+
:min_max_error => %Q|:minimum should be less than :maximum!|,
|
41
|
+
:bad_range_error => %Q|Your :count range(%s) has no sence!|,
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
def initialize tag, options={}, &block
|
46
|
+
@tag, @options, @block = tag.to_s, options, block
|
47
|
+
|
48
|
+
if with_attrs = @options.delete(:with)
|
49
|
+
if classes = with_attrs.delete(:class)
|
50
|
+
@tag += '.' + classes_to_selector(classes)
|
51
|
+
end
|
52
|
+
selector = with_attrs.inject('') do |html_attrs_string, (k, v)|
|
53
|
+
html_attrs_string += "[#{k}='#{v}']"
|
54
|
+
html_attrs_string
|
55
|
+
end
|
56
|
+
@tag += selector
|
57
|
+
end
|
58
|
+
|
59
|
+
if without_attrs = @options.delete(:without)
|
60
|
+
if classes = without_attrs.delete(:class)
|
61
|
+
@tag += ":not(.#{classes_to_selector(classes)})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
validate_options!
|
66
|
+
set_options
|
67
|
+
end
|
68
|
+
|
69
|
+
def matches? document, &block
|
70
|
+
@block = block if block
|
71
|
+
|
72
|
+
document = document.html if defined?(Capybara::Session) && document.is_a?(Capybara::Session)
|
73
|
+
|
74
|
+
case document
|
75
|
+
when String
|
76
|
+
@parent_scope = Nokogiri::HTML(document)
|
77
|
+
@document = document
|
78
|
+
else
|
79
|
+
@parent_scope = document.current_scope
|
80
|
+
@document = @parent_scope.to_html
|
81
|
+
end
|
82
|
+
@current_scope = begin
|
83
|
+
@parent_scope.css(@tag)
|
84
|
+
# on jruby this produce exception if css was not found:
|
85
|
+
# undefined method `decorate' for nil:NilClass
|
86
|
+
rescue NoMethodError
|
87
|
+
Nokogiri::XML::NodeSet.new(Nokogiri::XML::Document.new)
|
88
|
+
end
|
89
|
+
if tag_presents? and text_right? and count_right?
|
90
|
+
@block.call(self) if @block
|
91
|
+
true
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def document
|
98
|
+
@document
|
99
|
+
end
|
100
|
+
|
101
|
+
def description
|
102
|
+
# TODO should it be more complicated?
|
103
|
+
if @options.has_key?(:count)
|
104
|
+
DESCRIPTIONS[:have_n] % [@options[:count],@tag]
|
105
|
+
else
|
106
|
+
DESCRIPTIONS[:have_at_least_1] % @tag
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def classes_to_selector(classes)
|
113
|
+
case classes
|
114
|
+
when Array
|
115
|
+
classes.join('.')
|
116
|
+
when String
|
117
|
+
classes.gsub(/\s+/, '.')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def tag_presents?
|
122
|
+
if @current_scope.first
|
123
|
+
@count = @current_scope.count
|
124
|
+
@failure_message_when_negated = MESSAGES[:unexpected_tag] % [@document, @tag, @count]
|
125
|
+
true
|
126
|
+
else
|
127
|
+
@failure_message = MESSAGES[:expected_tag] % [@document, @tag]
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def count_right?
|
133
|
+
case @options[:count]
|
134
|
+
when Integer
|
135
|
+
((@failure_message_when_negated=MESSAGES[:unexpected_count] % [@document,@count,@tag]) && @count == @options[:count]) || (@failure_message=MESSAGES[:expected_count] % [@document,@options[:count],@tag,@count]; false)
|
136
|
+
when Range
|
137
|
+
((@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)
|
138
|
+
when nil
|
139
|
+
if @options[:maximum]
|
140
|
+
((@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)
|
141
|
+
elsif @options[:minimum]
|
142
|
+
((@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)
|
143
|
+
else
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def text_right?
|
150
|
+
return true unless @options[:text]
|
151
|
+
|
152
|
+
case text=@options[:text]
|
153
|
+
when Regexp
|
154
|
+
new_scope = @current_scope.css(':regexp()',NokogiriRegexpHelper.new(text))
|
155
|
+
unless new_scope.empty?
|
156
|
+
@count = new_scope.count
|
157
|
+
@failure_message_when_negated = MESSAGES[:unexpected_regexp] % [text.inspect,@tag,@document]
|
158
|
+
true
|
159
|
+
else
|
160
|
+
@failure_message = MESSAGES[:expected_regexp] % [text.inspect,@tag,@document]
|
161
|
+
false
|
162
|
+
end
|
163
|
+
else
|
164
|
+
new_scope = @current_scope.css(':content()', NokogiriTextHelper.new(text, @options[:squeeze_text]))
|
165
|
+
unless new_scope.empty?
|
166
|
+
@count = new_scope.count
|
167
|
+
@failure_message_when_negated = MESSAGES[:unexpected_text] % [text,@tag,@document]
|
168
|
+
true
|
169
|
+
else
|
170
|
+
@failure_message = MESSAGES[:expected_text] % [text,@tag,@document]
|
171
|
+
false
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
protected
|
177
|
+
|
178
|
+
def validate_options!
|
179
|
+
validate_count_presence!
|
180
|
+
validate_count_when_set_min_max!
|
181
|
+
validate_count_when_set_range!
|
182
|
+
end
|
183
|
+
|
184
|
+
def validate_count_presence!
|
185
|
+
raise 'wrong :count specified' unless [Range, NilClass].include?(@options[:count].class) or @options[:count].is_a?(Integer)
|
186
|
+
|
187
|
+
[:min, :minimum, :max, :maximum].each do |key|
|
188
|
+
raise MESSAGES[:wrong_count_error] if @options.has_key?(key) and @options.has_key?(:count)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def validate_count_when_set_min_max!
|
193
|
+
begin
|
194
|
+
raise MESSAGES[:min_max_error] if @options[:minimum] > @options[:maximum]
|
195
|
+
rescue NoMethodError # nil > 4
|
196
|
+
rescue ArgumentError # 2 < nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def validate_count_when_set_range!
|
201
|
+
begin
|
202
|
+
begin
|
203
|
+
raise MESSAGES[:bad_range_error] % [@options[:count].to_s] if count_is_range_but_no_min?
|
204
|
+
rescue ArgumentError, "comparison of String with" # if @options[:count] == 'a'..'z'
|
205
|
+
raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
|
206
|
+
end
|
207
|
+
rescue TypeError # fix for 1.8.7 for 'rescue ArgumentError, "comparison of String with"' stroke
|
208
|
+
raise MESSAGES[:bad_range_error] % [@options[:count].to_s]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def count_is_range_but_no_min?
|
213
|
+
@options[:count] && @options[:count].is_a?(Range) &&
|
214
|
+
(@options[:count].min.nil? or @options[:count].min < 0)
|
215
|
+
end
|
216
|
+
|
217
|
+
def set_options
|
218
|
+
@options[:minimum] ||= @options.delete(:min)
|
219
|
+
@options[:maximum] ||= @options.delete(:max)
|
220
|
+
|
221
|
+
@options[:text] = @options[:text].to_s if @options.has_key?(:text) && !@options[:text].is_a?(Regexp)
|
222
|
+
|
223
|
+
if @options.has_key?(:seen) && !@options[:seen].is_a?(Regexp)
|
224
|
+
@options[:text] = @options[:seen].to_s
|
225
|
+
@options[:squeeze_text] = true
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
module RSpecHtmlMatchers
|
5
|
+
|
6
|
+
# @api
|
7
|
+
# @private
|
8
|
+
# for nokogiri regexp matching
|
9
|
+
class NokogiriRegexpHelper
|
10
|
+
def initialize(regex)
|
11
|
+
@regex = regex
|
12
|
+
end
|
13
|
+
|
14
|
+
def regexp node_set
|
15
|
+
node_set.find_all { |node| node.content =~ @regex }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
module RSpecHtmlMatchers
|
5
|
+
|
6
|
+
# @api
|
7
|
+
# @private
|
8
|
+
class NokogiriTextHelper
|
9
|
+
NON_BREAKING_SPACE = "\u00a0"
|
10
|
+
|
11
|
+
def initialize text, squeeze_text = false
|
12
|
+
@text = text
|
13
|
+
@squeeze_text = squeeze_text
|
14
|
+
end
|
15
|
+
|
16
|
+
def content node_set
|
17
|
+
node_set.find_all do |node|
|
18
|
+
actual_content = node.content.gsub(NON_BREAKING_SPACE, ' ')
|
19
|
+
actual_content = node.content.strip.squeeze(' ') if @squeeze_text
|
20
|
+
|
21
|
+
actual_content == @text
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/spec/form_matchers_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe "have_form" do
|
|
7
7
|
it "should find form" do
|
8
8
|
# sanity check
|
9
9
|
expect(rendered).to have_form("/books", :post)
|
10
|
-
expect(rendered).to have_form("/books", "post", with
|
10
|
+
expect(rendered).to have_form("/books", "post", :with => { :id => "new_book", :class => %w(book formtastic) })
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should not find form" do
|
@@ -20,15 +20,15 @@ describe "have_form" do
|
|
20
20
|
context "with_select" do
|
21
21
|
it "should find select" do
|
22
22
|
expect(rendered).to have_form("/books", :post) do
|
23
|
-
with_select("book[publisher_id]", with
|
24
|
-
with_select("book[publisher_id]", with
|
23
|
+
with_select("book[publisher_id]", :with => { :id => "book_publisher_id" })
|
24
|
+
with_select("book[publisher_id]", :with => { :id => "book_publisher_id" })
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
it "should not find select" do
|
29
29
|
expect(rendered).to have_form("/books", :post) do
|
30
|
-
without_select("book[publisher_id]", with
|
31
|
-
without_select("koob[publisher_id]", with
|
30
|
+
without_select("book[publisher_id]", :with => { :id => "other_id" })
|
31
|
+
without_select("koob[publisher_id]", :with => { :id => "book_publisher_id" })
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -37,9 +37,9 @@ describe "have_form" do
|
|
37
37
|
expect(rendered).to have_form("/books", :post) do
|
38
38
|
with_select("book[publisher_id]") do
|
39
39
|
with_option(nil)
|
40
|
-
with_option("The Pragmatic Bookshelf", selected
|
40
|
+
with_option("The Pragmatic Bookshelf", :selected => true)
|
41
41
|
with_option(/sitepoint/,2)
|
42
|
-
with_option("O'Reilly", 3, selected
|
42
|
+
with_option("O'Reilly", 3, :selected => false)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -48,7 +48,7 @@ describe "have_form" do
|
|
48
48
|
expect(rendered).to have_form("/books", :post) do
|
49
49
|
with_select("book[publisher_id]") do
|
50
50
|
without_option("blah blah")
|
51
|
-
without_option("O'Reilly", 3, selected
|
51
|
+
without_option("O'Reilly", 3, :selected => true)
|
52
52
|
without_option("O'Reilly", 100500)
|
53
53
|
end
|
54
54
|
end
|
@@ -163,8 +163,8 @@ describe "have_form" do
|
|
163
163
|
expect(rendered).to have_form("/books", :post) do
|
164
164
|
with_range_field('range1', 1, 3)
|
165
165
|
with_range_field('range1','1','3')
|
166
|
-
with_range_field('range2', 1, 3, with
|
167
|
-
with_range_field('range2', 1, 3, with
|
166
|
+
with_range_field('range2', 1, 3, :with => { :value => 2 } )
|
167
|
+
with_range_field('range2', 1, 3, :with => { :value => '2' } )
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -172,7 +172,7 @@ describe "have_form" do
|
|
172
172
|
expect(rendered).to have_form("/books", :post) do
|
173
173
|
without_range_field('number')
|
174
174
|
without_range_field('range1', 1, 5)
|
175
|
-
without_range_field('range2', 1, 3, with
|
175
|
+
without_range_field('range2', 1, 3, :with => { :value => 5 } )
|
176
176
|
end
|
177
177
|
end
|
178
178
|
end
|
@@ -182,7 +182,7 @@ describe "have_form" do
|
|
182
182
|
expect(rendered).to have_form("/books", :post) do
|
183
183
|
with_date_field(:date)
|
184
184
|
with_date_field(:date, 'book_date')
|
185
|
-
with_date_field(:month, 'book_month', with
|
185
|
+
with_date_field(:month, 'book_month', :with => { :value => 5 })
|
186
186
|
with_date_field(:week,'book_week')
|
187
187
|
with_date_field(:time, 'book_time')
|
188
188
|
with_date_field(:datetime, 'book_datetime')
|
@@ -193,7 +193,7 @@ describe "have_form" do
|
|
193
193
|
it "should not find date field" do
|
194
194
|
expect(rendered).to have_form("/books", :post) do
|
195
195
|
without_date_field(:date, 'book_something')
|
196
|
-
without_date_field(:month, 'book_month', with
|
196
|
+
without_date_field(:month, 'book_month', :with => { :value => 100500 })
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
data/spec/have_empty_tag_spec.rb
CHANGED
@@ -6,9 +6,9 @@ describe 'have_empty_tag' do
|
|
6
6
|
asset 'single_element'
|
7
7
|
|
8
8
|
it { expect(rendered).to have_empty_tag('div') }
|
9
|
-
it { expect(rendered).to have_empty_tag('div', class
|
10
|
-
it { expect(rendered).to have_empty_tag('div', class
|
11
|
-
it { expect(rendered).to have_empty_tag('div', class
|
9
|
+
it { expect(rendered).to have_empty_tag('div', :class => "foo") }
|
10
|
+
it { expect(rendered).to have_empty_tag('div', :class => "bar") }
|
11
|
+
it { expect(rendered).to have_empty_tag('div', :class => "foo bar") }
|
12
12
|
end
|
13
13
|
|
14
14
|
context 'when paragraphs' do
|
@@ -23,7 +23,7 @@ describe 'have_empty_tag' do
|
|
23
23
|
it { expect(rendered).to_not have_empty_tag('html') }
|
24
24
|
it { expect(rendered).to_not have_empty_tag('body') }
|
25
25
|
it { expect(rendered).to_not have_empty_tag('ol') }
|
26
|
-
it { expect(rendered).to_not have_empty_tag('ol', class
|
26
|
+
it { expect(rendered).to_not have_empty_tag('ol', :class => 'menu') }
|
27
27
|
it { expect(rendered).to_not have_empty_tag('li') }
|
28
28
|
end
|
29
29
|
end
|
data/spec/have_tag_spec.rb
CHANGED
@@ -295,7 +295,9 @@ describe 'have_tag' do
|
|
295
295
|
expect(rendered).to have_tag('p', :seen => 'content with ignored spaces in')
|
296
296
|
expect(rendered).to have_tag('p', :seen => 'content with nbsp and spaces around')
|
297
297
|
|
298
|
-
|
298
|
+
unless Nokogiri::VERSION == '1.5.11'
|
299
|
+
expect(rendered).to have_tag('p', :text => 'content with nbsp')
|
300
|
+
end
|
299
301
|
expect(rendered).to have_tag('pre', :text => " 1. bla \n 2. bla ")
|
300
302
|
end
|
301
303
|
|
@@ -425,9 +427,10 @@ describe 'have_tag' do
|
|
425
427
|
with_text /sample with 'single' and "double" quotes/
|
426
428
|
end
|
427
429
|
|
428
|
-
|
429
|
-
|
430
|
-
|
430
|
+
unless Nokogiri::VERSION == '1.5.11'
|
431
|
+
expect(rendered).to have_tag('p') do
|
432
|
+
with_text 'content with nbsp'
|
433
|
+
end
|
431
434
|
end
|
432
435
|
|
433
436
|
expect(rendered).to have_tag('pre') do
|
@@ -634,22 +637,22 @@ describe 'have_tag' do
|
|
634
637
|
|
635
638
|
it 'with block parameters' do
|
636
639
|
expect(rendered).to have_tag('div#one') do |a|
|
637
|
-
expect(a).to have_tag 'p.find_me', count
|
640
|
+
expect(a).to have_tag 'p.find_me', :count => 2
|
638
641
|
|
639
|
-
expect(a).to have_tag 'b.nested', count
|
640
|
-
expect(a).to have_tag('p.deep-nesting', count
|
641
|
-
expect(b).to have_tag 'b.nested', count
|
642
|
+
expect(a).to have_tag 'b.nested', :count => 3
|
643
|
+
expect(a).to have_tag('p.deep-nesting', :count => 1) do |b|
|
644
|
+
expect(b).to have_tag 'b.nested', :count => 2
|
642
645
|
end
|
643
646
|
end
|
644
647
|
end
|
645
648
|
|
646
649
|
it 'with short_hand methods' do
|
647
650
|
expect(rendered).to have_tag('div#one') do
|
648
|
-
with_tag 'p.find_me', count
|
651
|
+
with_tag 'p.find_me', :count => 2
|
649
652
|
|
650
|
-
with_tag 'b.nested', count
|
651
|
-
with_tag('p.deep-nesting', count
|
652
|
-
with_tag 'b.nested', count
|
653
|
+
with_tag 'b.nested', :count => 3
|
654
|
+
with_tag('p.deep-nesting', :count => 1) do
|
655
|
+
with_tag 'b.nested', :count => 2
|
653
656
|
end
|
654
657
|
end
|
655
658
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-html-matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kucaahbe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -162,6 +162,7 @@ description: 'Nokogiri based ''have_tag'' and ''with_tag'' matchers for rspec 3.
|
|
162
162
|
'
|
163
163
|
email:
|
164
164
|
- kucaahbe@ukr.net
|
165
|
+
- randoum@gmail.com
|
165
166
|
executables: []
|
166
167
|
extensions: []
|
167
168
|
extra_rdoc_files:
|
@@ -174,6 +175,10 @@ files:
|
|
174
175
|
- features/step_definitions/steps.rb
|
175
176
|
- features/support/env.rb
|
176
177
|
- lib/rspec-html-matchers.rb
|
178
|
+
- lib/rspec-html-matchers/have_tag.rb
|
179
|
+
- lib/rspec-html-matchers/nokogiri_regexp_helper.rb
|
180
|
+
- lib/rspec-html-matchers/nokogiri_text_helper.rb
|
181
|
+
- lib/rspec-html-matchers/version.rb
|
177
182
|
- spec/form_matchers_spec.rb
|
178
183
|
- spec/have_empty_tag_spec.rb
|
179
184
|
- spec/have_tag_spec.rb
|
@@ -192,7 +197,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
197
|
requirements:
|
193
198
|
- - ">="
|
194
199
|
- !ruby/object:Gem::Version
|
195
|
-
version:
|
200
|
+
version: 1.8.7
|
196
201
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
202
|
requirements:
|
198
203
|
- - ">="
|
@@ -200,17 +205,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
205
|
version: '0'
|
201
206
|
requirements: []
|
202
207
|
rubyforge_project:
|
203
|
-
rubygems_version: 2.
|
208
|
+
rubygems_version: 2.5.2
|
204
209
|
signing_key:
|
205
210
|
specification_version: 4
|
206
211
|
summary: Nokogiri based 'have_tag' and 'with_tag' matchers for rspec 3
|
207
212
|
test_files:
|
208
|
-
- spec/
|
213
|
+
- spec/spec_helper.rb
|
209
214
|
- spec/have_empty_tag_spec.rb
|
215
|
+
- spec/form_matchers_spec.rb
|
210
216
|
- spec/have_tag_spec.rb
|
211
|
-
- spec/spec_helper.rb
|
212
|
-
- spec/support/asset_helpers.rb
|
213
217
|
- spec/support/raise_spec_error_helper.rb
|
218
|
+
- spec/support/asset_helpers.rb
|
214
219
|
- features/step_definitions/steps.rb
|
215
220
|
- features/support/env.rb
|
216
221
|
- features/js_generated_content.feature
|