capybara 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +21 -0
- data/README.md +50 -12
- data/lib/capybara.rb +8 -1
- data/lib/capybara/driver/base.rb +28 -0
- data/lib/capybara/driver/node.rb +3 -2
- data/lib/capybara/helpers.rb +2 -3
- data/lib/capybara/node/actions.rb +5 -2
- data/lib/capybara/node/base.rb +10 -0
- data/lib/capybara/node/document.rb +2 -0
- data/lib/capybara/node/document_matchers.rb +68 -0
- data/lib/capybara/node/element.rb +17 -2
- data/lib/capybara/node/finders.rb +5 -20
- data/lib/capybara/node/matchers.rb +101 -71
- data/lib/capybara/node/simple.rb +9 -15
- data/lib/capybara/queries/base_query.rb +29 -0
- data/lib/capybara/queries/text_query.rb +56 -0
- data/lib/capybara/queries/title_query.rb +40 -0
- data/lib/capybara/query.rb +30 -20
- data/lib/capybara/rack_test/node.rb +11 -3
- data/lib/capybara/result.rb +1 -1
- data/lib/capybara/rspec/features.rb +38 -21
- data/lib/capybara/rspec/matchers.rb +53 -38
- data/lib/capybara/selector.rb +68 -14
- data/lib/capybara/selenium/driver.rb +54 -6
- data/lib/capybara/selenium/node.rb +4 -2
- data/lib/capybara/session.rb +109 -35
- data/lib/capybara/spec/public/test.js +34 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +57 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +19 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +49 -0
- data/lib/capybara/spec/session/assert_text.rb +195 -0
- data/lib/capybara/spec/session/assert_title.rb +69 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +35 -0
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +19 -0
- data/lib/capybara/spec/session/find_field_spec.rb +6 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -1
- data/lib/capybara/spec/session/node_spec.rb +16 -1
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +5 -0
- data/lib/capybara/spec/views/with_html.erb +4 -0
- data/lib/capybara/spec/views/with_js.erb +17 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/dsl_spec.rb +3 -1
- data/spec/rack_test_spec.rb +12 -1
- data/spec/rspec/features_spec.rb +1 -1
- data/spec/rspec/matchers_spec.rb +113 -20
- data/spec/selenium_spec.rb +10 -1
- metadata +13 -2
@@ -91,8 +91,11 @@ module Capybara
|
|
91
91
|
def assert_selector(*args)
|
92
92
|
query = Capybara::Query.new(*args)
|
93
93
|
synchronize(query.wait) do
|
94
|
-
result =
|
95
|
-
|
94
|
+
result = query.resolve_for(self)
|
95
|
+
matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
|
96
|
+
unless matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
|
97
|
+
raise Capybara::ExpectationNotMet, result.failure_message
|
98
|
+
end
|
96
99
|
end
|
97
100
|
return true
|
98
101
|
end
|
@@ -116,19 +119,14 @@ module Capybara
|
|
116
119
|
def assert_no_selector(*args)
|
117
120
|
query = Capybara::Query.new(*args)
|
118
121
|
synchronize(query.wait) do
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
else
|
124
|
-
if result.size > 0 || (result.size == 0 && Capybara::Helpers.expects_none?(query.options))
|
125
|
-
raise(Capybara::ExpectationNotMet, result.negative_failure_message)
|
126
|
-
end
|
122
|
+
result = query.resolve_for(self)
|
123
|
+
matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
|
124
|
+
if matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
|
125
|
+
raise Capybara::ExpectationNotMet, result.negative_failure_message
|
127
126
|
end
|
128
127
|
end
|
129
128
|
return true
|
130
129
|
end
|
131
|
-
|
132
130
|
alias_method :refute_selector, :assert_no_selector
|
133
131
|
|
134
132
|
##
|
@@ -215,58 +213,6 @@ module Capybara
|
|
215
213
|
has_no_selector?(:css, path, options)
|
216
214
|
end
|
217
215
|
|
218
|
-
##
|
219
|
-
#
|
220
|
-
# Checks if the page or current node has the given text content,
|
221
|
-
# ignoring any HTML tags and normalizing whitespace.
|
222
|
-
#
|
223
|
-
# By default it will check if the text occurs at least once,
|
224
|
-
# but a different number can be specified.
|
225
|
-
#
|
226
|
-
# page.has_text?('lorem ipsum', between: 2..4)
|
227
|
-
#
|
228
|
-
# This will check if the text occurs from 2 to 4 times.
|
229
|
-
#
|
230
|
-
# @overload has_text?([type], text, [options])
|
231
|
-
# @param [:all, :visible] type Whether to check for only visible or all text
|
232
|
-
# @param [String, Regexp] text The text/regexp to check for
|
233
|
-
# @param [Hash] options additional options
|
234
|
-
# @option options [Integer] :count (nil) Number of times the text should occur
|
235
|
-
# @option options [Integer] :minimum (nil) Minimum number of times the text should occur
|
236
|
-
# @option options [Integer] :maximum (nil) Maximum number of times the text should occur
|
237
|
-
# @option options [Range] :between (nil) Range of times that should contain number of times text occurs
|
238
|
-
# @return [Boolean] Whether it exists
|
239
|
-
#
|
240
|
-
def has_text?(*args)
|
241
|
-
query = Capybara::Query.new(*args)
|
242
|
-
synchronize(query.wait) do
|
243
|
-
raise ExpectationNotMet unless text_found?(*args)
|
244
|
-
end
|
245
|
-
return true
|
246
|
-
rescue Capybara::ExpectationNotMet
|
247
|
-
return false
|
248
|
-
end
|
249
|
-
alias_method :has_content?, :has_text?
|
250
|
-
|
251
|
-
##
|
252
|
-
#
|
253
|
-
# Checks if the page or current node does not have the given text
|
254
|
-
# content, ignoring any HTML tags and normalizing whitespace.
|
255
|
-
#
|
256
|
-
# @param (see #has_text?)
|
257
|
-
# @return [Boolean] Whether it doesn't exist
|
258
|
-
#
|
259
|
-
def has_no_text?(*args)
|
260
|
-
query = Capybara::Query.new(*args)
|
261
|
-
synchronize(query.wait) do
|
262
|
-
raise ExpectationNotMet if text_found?(*args)
|
263
|
-
end
|
264
|
-
return true
|
265
|
-
rescue Capybara::ExpectationNotMet
|
266
|
-
return false
|
267
|
-
end
|
268
|
-
alias_method :has_no_content?, :has_no_text?
|
269
|
-
|
270
216
|
##
|
271
217
|
#
|
272
218
|
# Checks if the page or current node has a link with the given
|
@@ -479,18 +425,102 @@ module Capybara
|
|
479
425
|
has_no_selector?(:table, locator, options)
|
480
426
|
end
|
481
427
|
|
482
|
-
|
483
|
-
|
428
|
+
##
|
429
|
+
# Asserts that the page or current node has the given text content,
|
430
|
+
# ignoring any HTML tags.
|
431
|
+
#
|
432
|
+
# @!macro text_query_params
|
433
|
+
# @overload $0(type, text, options = {})
|
434
|
+
# @param [:all, :visible] type Whether to check for only visible or all text
|
435
|
+
# @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
|
436
|
+
# @option options [Integer] :count (nil) Number of times the text is expected to occur
|
437
|
+
# @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
|
438
|
+
# @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
|
439
|
+
# @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
|
440
|
+
# @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for text to eq/match given string/regexp argument
|
441
|
+
# @overload $0(text, options = {})
|
442
|
+
# @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
|
443
|
+
# @option options [Integer] :count (nil) Number of times the text is expected to occur
|
444
|
+
# @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
|
445
|
+
# @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
|
446
|
+
# @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
|
447
|
+
# @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for text to eq/match given string/regexp argument
|
448
|
+
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
449
|
+
# @return [true]
|
450
|
+
#
|
451
|
+
def assert_text(*args)
|
452
|
+
query = Capybara::Queries::TextQuery.new(*args)
|
453
|
+
synchronize(query.wait) do
|
454
|
+
count = query.resolve_for(self)
|
455
|
+
matches_count = Capybara::Helpers.matches_count?(count, query.options)
|
456
|
+
unless matches_count && ((count > 0) || Capybara::Helpers.expects_none?(query.options))
|
457
|
+
raise Capybara::ExpectationNotMet, query.failure_message
|
458
|
+
end
|
459
|
+
end
|
460
|
+
return true
|
484
461
|
end
|
485
462
|
|
486
|
-
|
463
|
+
##
|
464
|
+
# Asserts that the page or current node doesn't have the given text content,
|
465
|
+
# ignoring any HTML tags.
|
466
|
+
#
|
467
|
+
# @macro text_query_params
|
468
|
+
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
469
|
+
# @return [true]
|
470
|
+
#
|
471
|
+
def assert_no_text(*args)
|
472
|
+
query = Capybara::Queries::TextQuery.new(*args)
|
473
|
+
synchronize(query.wait) do
|
474
|
+
count = query.resolve_for(self)
|
475
|
+
matches_count = Capybara::Helpers.matches_count?(count, query.options)
|
476
|
+
if matches_count && ((count > 0) || Capybara::Helpers.expects_none?(query.options))
|
477
|
+
raise Capybara::ExpectationNotMet, query.negative_failure_message
|
478
|
+
end
|
479
|
+
end
|
480
|
+
return true
|
481
|
+
end
|
487
482
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
483
|
+
##
|
484
|
+
# Checks if the page or current node has the given text content,
|
485
|
+
# ignoring any HTML tags.
|
486
|
+
#
|
487
|
+
# Whitespaces are normalized in both node's text and passed text parameter.
|
488
|
+
# Note that whitespace isn't normalized in passed regexp as normalizing whitespace
|
489
|
+
# in regexp isn't easy and doesn't seem to be worth it.
|
490
|
+
#
|
491
|
+
# By default it will check if the text occurs at least once,
|
492
|
+
# but a different number can be specified.
|
493
|
+
#
|
494
|
+
# page.has_text?('lorem ipsum', between: 2..4)
|
495
|
+
#
|
496
|
+
# This will check if the text occurs from 2 to 4 times.
|
497
|
+
#
|
498
|
+
# @macro text_query_params
|
499
|
+
# @return [Boolean] Whether it exists
|
500
|
+
#
|
501
|
+
def has_text?(*args)
|
502
|
+
assert_text(*args)
|
503
|
+
rescue Capybara::ExpectationNotMet
|
504
|
+
return false
|
505
|
+
end
|
506
|
+
alias_method :has_content?, :has_text?
|
492
507
|
|
493
|
-
|
508
|
+
##
|
509
|
+
# Checks if the page or current node does not have the given text
|
510
|
+
# content, ignoring any HTML tags and normalizing whitespace.
|
511
|
+
#
|
512
|
+
# @macro text_query_params
|
513
|
+
# @return [Boolean] Whether it doesn't exist
|
514
|
+
#
|
515
|
+
def has_no_text?(*args)
|
516
|
+
assert_no_text(*args)
|
517
|
+
rescue Capybara::ExpectationNotMet
|
518
|
+
return false
|
519
|
+
end
|
520
|
+
alias_method :has_no_content?, :has_no_text?
|
521
|
+
|
522
|
+
def ==(other)
|
523
|
+
self.eql?(other) || (other.respond_to?(:base) && base == other.base)
|
494
524
|
end
|
495
525
|
end
|
496
526
|
end
|
data/lib/capybara/node/simple.rb
CHANGED
@@ -14,6 +14,7 @@ module Capybara
|
|
14
14
|
class Simple
|
15
15
|
include Capybara::Node::Finders
|
16
16
|
include Capybara::Node::Matchers
|
17
|
+
include Capybara::Node::DocumentMatchers
|
17
18
|
|
18
19
|
attr_reader :native
|
19
20
|
|
@@ -148,25 +149,18 @@ module Capybara
|
|
148
149
|
native.xpath("//title").first.text
|
149
150
|
end
|
150
151
|
|
151
|
-
def
|
152
|
-
|
152
|
+
def inspect
|
153
|
+
%(#<Capybara::Node::Simple tag="#{tag_name}" path="#{path}">)
|
153
154
|
end
|
154
155
|
|
155
|
-
|
156
|
-
|
156
|
+
# @api private
|
157
|
+
def find_css(css)
|
158
|
+
native.css(css)
|
157
159
|
end
|
158
160
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
elements = if query.selector.format == :css
|
163
|
-
native.css(query.css)
|
164
|
-
else
|
165
|
-
native.xpath(query.xpath(exact))
|
166
|
-
end.map do |node|
|
167
|
-
self.class.new(node)
|
168
|
-
end
|
169
|
-
Capybara::Result.new(elements, query)
|
161
|
+
# @api private
|
162
|
+
def find_xpath(xpath)
|
163
|
+
native.xpath(xpath)
|
170
164
|
end
|
171
165
|
end
|
172
166
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Capybara
|
2
|
+
# @api private
|
3
|
+
module Queries
|
4
|
+
class BaseQuery
|
5
|
+
COUNT_KEYS = [:count, :minimum, :maximum, :between]
|
6
|
+
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def wait
|
10
|
+
if @options.has_key?(:wait)
|
11
|
+
@options[:wait] || 0
|
12
|
+
else
|
13
|
+
Capybara.default_wait_time
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def assert_valid_keys
|
20
|
+
invalid_keys = @options.keys - valid_keys
|
21
|
+
unless invalid_keys.empty?
|
22
|
+
invalid_names = invalid_keys.map(&:inspect).join(", ")
|
23
|
+
valid_names = valid_keys.map(&:inspect).join(", ")
|
24
|
+
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Capybara
|
2
|
+
# @api private
|
3
|
+
module Queries
|
4
|
+
class TextQuery < BaseQuery
|
5
|
+
def initialize(*args)
|
6
|
+
@type = args.shift if args.first.is_a?(Symbol) || args.first.nil?
|
7
|
+
@expected_text, @options = args
|
8
|
+
unless @expected_text.is_a?(Regexp)
|
9
|
+
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
10
|
+
end
|
11
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_text)
|
12
|
+
@options ||= {}
|
13
|
+
assert_valid_keys
|
14
|
+
|
15
|
+
# this is needed to not break existing tests that may use keys supported by `Query` but not supported by `TextQuery`
|
16
|
+
# can be removed in next minor version (> 2.4)
|
17
|
+
invalid_keys = @options.keys - (COUNT_KEYS + [:wait])
|
18
|
+
unless invalid_keys.empty?
|
19
|
+
invalid_names = invalid_keys.map(&:inspect).join(", ")
|
20
|
+
valid_names = valid_keys.map(&:inspect).join(", ")
|
21
|
+
warn "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolve_for(node)
|
26
|
+
@actual_text = Capybara::Helpers.normalize_whitespace(node.text(@type))
|
27
|
+
@count = @actual_text.scan(@search_regexp).size
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message
|
31
|
+
description =
|
32
|
+
if @expected_text.is_a?(Regexp)
|
33
|
+
"text matching #{@expected_text.inspect}"
|
34
|
+
else
|
35
|
+
"text #{@expected_text.inspect}"
|
36
|
+
end
|
37
|
+
|
38
|
+
message = Capybara::Helpers.failure_message(description, @options)
|
39
|
+
unless (COUNT_KEYS & @options.keys).empty?
|
40
|
+
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
41
|
+
end
|
42
|
+
message << " in #{@actual_text.inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def negative_failure_message
|
46
|
+
failure_message.sub(/(to find)/, 'not \1')
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def valid_keys
|
52
|
+
Capybara::Query::VALID_KEYS # can be changed to COUNT_KEYS + [:wait] in next minor version (> 2.4)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Capybara
|
2
|
+
# @api private
|
3
|
+
module Queries
|
4
|
+
class TitleQuery < BaseQuery
|
5
|
+
def initialize(expected_title, options = {})
|
6
|
+
@expected_title = expected_title
|
7
|
+
@options = options
|
8
|
+
unless @expected_title.is_a?(Regexp)
|
9
|
+
@expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
|
10
|
+
end
|
11
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_title)
|
12
|
+
assert_valid_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolves_for?(node)
|
16
|
+
@actual_title = node.title
|
17
|
+
@actual_title.match(@search_regexp)
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure_message
|
21
|
+
failure_message_helper
|
22
|
+
end
|
23
|
+
|
24
|
+
def negative_failure_message
|
25
|
+
failure_message_helper(' not')
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def failure_message_helper(negated = '')
|
31
|
+
verb = (@expected_title.is_a?(Regexp))? 'match' : 'include'
|
32
|
+
"expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid_keys
|
36
|
+
[:wait]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/capybara/query.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Capybara
|
2
|
-
class
|
2
|
+
# @deprecated This class and its methods are not supposed to be used by users of Capybara's public API.
|
3
|
+
# It may be removed in future versions of Capybara.
|
4
|
+
class Query < Queries::BaseQuery
|
3
5
|
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
4
6
|
|
5
7
|
VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
|
@@ -23,7 +25,7 @@ module Capybara
|
|
23
25
|
end
|
24
26
|
|
25
27
|
@expression = @selector.call(@locator)
|
26
|
-
assert_valid_keys
|
28
|
+
assert_valid_keys
|
27
29
|
end
|
28
30
|
|
29
31
|
def name; selector.name; end
|
@@ -32,7 +34,7 @@ module Capybara
|
|
32
34
|
def description
|
33
35
|
@description = "#{label} #{locator.inspect}"
|
34
36
|
@description << " with text #{options[:text].inspect}" if options[:text]
|
35
|
-
@description <<
|
37
|
+
@description << selector.description(options)
|
36
38
|
@description
|
37
39
|
end
|
38
40
|
|
@@ -70,14 +72,6 @@ module Capybara
|
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
def wait
|
74
|
-
if options.has_key?(:wait)
|
75
|
-
@options[:wait] or 0
|
76
|
-
else
|
77
|
-
Capybara.default_wait_time
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
75
|
def exact?
|
82
76
|
if options.has_key?(:exact)
|
83
77
|
@options[:exact]
|
@@ -107,16 +101,32 @@ module Capybara
|
|
107
101
|
@expression
|
108
102
|
end
|
109
103
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
104
|
+
# @api private
|
105
|
+
def resolve_for(node, exact = nil)
|
106
|
+
node.synchronize do
|
107
|
+
children = if selector.format == :css
|
108
|
+
node.find_css(self.css)
|
109
|
+
else
|
110
|
+
node.find_xpath(self.xpath(exact))
|
111
|
+
end.map do |child|
|
112
|
+
if node.is_a?(Capybara::Node::Base)
|
113
|
+
Capybara::Node::Element.new(node.session, child, node, self)
|
114
|
+
else
|
115
|
+
Capybara::Node::Simple.new(child)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
Capybara::Result.new(children, self)
|
119
119
|
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def valid_keys
|
125
|
+
COUNT_KEYS + [:text, :visible, :exact, :match, :wait] + @selector.custom_filters.keys
|
126
|
+
end
|
127
|
+
|
128
|
+
def assert_valid_keys
|
129
|
+
super
|
120
130
|
unless VALID_MATCH.include?(match)
|
121
131
|
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
|
122
132
|
end
|