capybara 2.13.0 → 2.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/History.md +218 -18
- data/README.md +54 -23
- data/lib/capybara/config.rb +132 -0
- data/lib/capybara/cucumber.rb +1 -0
- data/lib/capybara/driver/base.rb +14 -0
- data/lib/capybara/dsl.rb +1 -3
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/minitest/spec.rb +14 -37
- data/lib/capybara/minitest.rb +95 -114
- data/lib/capybara/node/actions.rb +10 -10
- data/lib/capybara/node/base.rb +7 -2
- data/lib/capybara/node/element.rb +9 -3
- data/lib/capybara/node/finders.rb +92 -18
- data/lib/capybara/node/matchers.rb +21 -9
- data/lib/capybara/node/simple.rb +5 -0
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/base_query.rb +12 -3
- data/lib/capybara/queries/current_path_query.rb +13 -9
- data/lib/capybara/queries/selector_query.rb +62 -23
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/queries/text_query.rb +10 -5
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/rack_test/browser.rb +13 -5
- data/lib/capybara/rack_test/driver.rb +6 -1
- data/lib/capybara/rack_test/form.rb +4 -3
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/matcher_proxies.rb +45 -0
- data/lib/capybara/rspec/matchers.rb +108 -7
- data/lib/capybara/rspec.rb +3 -1
- data/lib/capybara/selector/filter.rb +13 -41
- data/lib/capybara/selector/filter_set.rb +30 -4
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +36 -15
- data/lib/capybara/selector.rb +63 -42
- data/lib/capybara/selenium/driver.rb +177 -33
- data/lib/capybara/selenium/node.rb +106 -55
- data/lib/capybara/server.rb +6 -5
- data/lib/capybara/session/config.rb +114 -0
- data/lib/capybara/session/matchers.rb +15 -4
- data/lib/capybara/session.rb +178 -65
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/test.js +18 -3
- data/lib/capybara/spec/session/accept_alert_spec.rb +9 -1
- data/lib/capybara/spec/session/accept_prompt_spec.rb +29 -1
- data/lib/capybara/spec/session/all_spec.rb +13 -1
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
- data/lib/capybara/spec/session/assert_selector.rb +1 -1
- data/lib/capybara/spec/session/assert_text.rb +8 -0
- data/lib/capybara/spec/session/assert_title.rb +22 -9
- data/lib/capybara/spec/session/attach_file_spec.rb +8 -1
- data/lib/capybara/spec/session/check_spec.rb +4 -4
- data/lib/capybara/spec/session/choose_spec.rb +2 -2
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
- data/lib/capybara/spec/session/click_link_spec.rb +1 -1
- data/lib/capybara/spec/session/current_url_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +22 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +8 -2
- data/lib/capybara/spec/session/find_field_spec.rb +1 -0
- data/lib/capybara/spec/session/find_spec.rb +8 -6
- data/lib/capybara/spec/session/first_spec.rb +10 -5
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +11 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +52 -7
- data/lib/capybara/spec/session/has_link_spec.rb +4 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +64 -6
- data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
- data/lib/capybara/spec/session/has_text_spec.rb +5 -3
- data/lib/capybara/spec/session/has_title_spec.rb +4 -2
- data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
- data/lib/capybara/spec/session/node_spec.rb +50 -26
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +3 -2
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
- data/lib/capybara/spec/session/unselect_spec.rb +2 -2
- data/lib/capybara/spec/session/visit_spec.rb +56 -1
- data/lib/capybara/spec/session/window/become_closed_spec.rb +11 -11
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
- data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
- data/lib/capybara/spec/spec_helper.rb +28 -4
- data/lib/capybara/spec/test_app.rb +3 -1
- data/lib/capybara/spec/views/form.erb +27 -1
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +5 -0
- data/lib/capybara/spec/views/with_html.erb +33 -2
- data/lib/capybara/spec/views/with_js.erb +12 -0
- data/lib/capybara/spec/views/with_windows.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +102 -124
- data/spec/capybara_spec.rb +43 -21
- data/spec/dsl_spec.rb +1 -0
- data/spec/filter_set_spec.rb +28 -0
- data/spec/minitest_spec.rb +9 -1
- data/spec/minitest_spec_spec.rb +19 -5
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/result_spec.rb +20 -0
- data/spec/rspec/shared_spec_matchers.rb +148 -44
- data/spec/rspec/views_spec.rb +4 -0
- data/spec/rspec_matchers_spec.rb +46 -0
- data/spec/rspec_spec.rb +77 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +25 -17
- data/spec/selenium_spec_firefox.rb +2 -1
- data/spec/selenium_spec_marionette.rb +18 -5
- data/spec/session_spec.rb +44 -0
- data/spec/shared_selenium_session.rb +72 -8
- data/spec/spec_helper.rb +4 -0
- metadata +55 -8
@@ -55,7 +55,7 @@ module Capybara
|
|
55
55
|
# @return [String] The text of the element
|
56
56
|
#
|
57
57
|
def text(type=nil)
|
58
|
-
type ||= :all unless
|
58
|
+
type ||= :all unless session_options.ignore_hidden_elements or session_options.visible_text_only
|
59
59
|
synchronize do
|
60
60
|
if type == :all
|
61
61
|
base.all_text
|
@@ -371,9 +371,15 @@ module Capybara
|
|
371
371
|
end
|
372
372
|
|
373
373
|
def inspect
|
374
|
-
%(#<Capybara::Node::Element tag="#{tag_name}" path="#{path}">)
|
374
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
|
375
375
|
rescue NotSupportedByDriverError
|
376
|
-
%(#<Capybara::Node::Element tag="#{tag_name}">)
|
376
|
+
%(#<Capybara::Node::Element tag="#{base.tag_name}">)
|
377
|
+
rescue => e
|
378
|
+
if session.driver.invalid_element_errors.any? { |et| e.is_a?(et)}
|
379
|
+
%(Obsolete #<Capybara::Node::Element>)
|
380
|
+
else
|
381
|
+
raise
|
382
|
+
end
|
377
383
|
end
|
378
384
|
end
|
379
385
|
end
|
@@ -29,22 +29,71 @@ module Capybara
|
|
29
29
|
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
30
30
|
#
|
31
31
|
def find(*args, &optional_filter_block)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
32
|
+
if args.last.is_a? Hash
|
33
|
+
args.last[:session_options] = session_options
|
34
|
+
else
|
35
|
+
args.push(session_options: session_options)
|
36
|
+
end
|
37
|
+
synced_resolve Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
#
|
42
|
+
# Find an {Capybara::Node::Element} based on the given arguments that is also an ancestor of the element called on. +ancestor+ will raise an error if the element
|
43
|
+
# is not found.
|
44
|
+
#
|
45
|
+
# +ancestor+ takes the same options as +find+.
|
46
|
+
#
|
47
|
+
# element.ancestor('#foo').find('.bar')
|
48
|
+
# element.ancestor(:xpath, './/div[contains(., "bar")]')
|
49
|
+
# element.ancestor('ul', text: 'Quox').click_link('Delete')
|
50
|
+
#
|
51
|
+
# @param (see Capybara::Node::Finders#find)
|
52
|
+
#
|
53
|
+
# @!macro waiting_behavior
|
54
|
+
#
|
55
|
+
# @option options [Boolean] match The matching strategy to use.
|
56
|
+
#
|
57
|
+
# @return [Capybara::Node::Element] The found element
|
58
|
+
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
59
|
+
#
|
60
|
+
def ancestor(*args, &optional_filter_block)
|
61
|
+
if args.last.is_a? Hash
|
62
|
+
args.last[:session_options] = session_options
|
63
|
+
else
|
64
|
+
args.push(session_options: session_options)
|
65
|
+
end
|
66
|
+
synced_resolve Capybara::Queries::AncestorQuery.new(*args, &optional_filter_block)
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
#
|
71
|
+
# Find an {Capybara::Node::Element} based on the given arguments that is also a sibling of the element called on. +sibling+ will raise an error if the element
|
72
|
+
# is not found.
|
73
|
+
#
|
74
|
+
#
|
75
|
+
# +sibling+ takes the same options as +find+.
|
76
|
+
#
|
77
|
+
# element.sibling('#foo').find('.bar')
|
78
|
+
# element.sibling(:xpath, './/div[contains(., "bar")]')
|
79
|
+
# element.sibling('ul', text: 'Quox').click_link('Delete')
|
80
|
+
#
|
81
|
+
# @param (see Capybara::Node::Finders#find)
|
82
|
+
#
|
83
|
+
# @macro waiting_behavior
|
84
|
+
#
|
85
|
+
# @option options [Boolean] match The matching strategy to use.
|
86
|
+
#
|
87
|
+
# @return [Capybara::Node::Element] The found element
|
88
|
+
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
89
|
+
#
|
90
|
+
def sibling(*args, &optional_filter_block)
|
91
|
+
if args.last.is_a? Hash
|
92
|
+
args.last[:session_options] = session_options
|
93
|
+
else
|
94
|
+
args.push(session_options: session_options)
|
95
|
+
end
|
96
|
+
synced_resolve Capybara::Queries::SiblingQuery.new(*args, &optional_filter_block)
|
48
97
|
end
|
49
98
|
|
50
99
|
##
|
@@ -122,7 +171,7 @@ module Capybara
|
|
122
171
|
# @option options [String] id Match buttons with the id provided
|
123
172
|
# @option options [String] title Match buttons with the title provided
|
124
173
|
# @option options [String] value Match buttons with the value provided
|
125
|
-
# @option options [String, Array<String>] class Match
|
174
|
+
# @option options [String, Array<String>] class Match buttons that match the class(es) provided
|
126
175
|
# @return [Capybara::Node::Element] The found element
|
127
176
|
#
|
128
177
|
def find_button(locator=nil, options={}, &optional_filter_block)
|
@@ -208,6 +257,11 @@ module Capybara
|
|
208
257
|
# @return [Capybara::Result] A collection of found elements
|
209
258
|
#
|
210
259
|
def all(*args, &optional_filter_block)
|
260
|
+
if args.last.is_a? Hash
|
261
|
+
args.last[:session_options] = session_options
|
262
|
+
else
|
263
|
+
args.push(session_options: session_options)
|
264
|
+
end
|
211
265
|
query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
|
212
266
|
synchronize(query.wait) do
|
213
267
|
result = query.resolve_for(self)
|
@@ -233,7 +287,7 @@ module Capybara
|
|
233
287
|
# @return [Capybara::Node::Element] The found element or nil
|
234
288
|
#
|
235
289
|
def first(*args, &optional_filter_block)
|
236
|
-
if
|
290
|
+
if session_options.wait_on_first_by_default
|
237
291
|
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
238
292
|
args.push({minimum: 1}.merge(options))
|
239
293
|
end
|
@@ -242,6 +296,26 @@ module Capybara
|
|
242
296
|
nil
|
243
297
|
end
|
244
298
|
|
299
|
+
private
|
300
|
+
|
301
|
+
def synced_resolve(query)
|
302
|
+
synchronize(query.wait) do
|
303
|
+
if (query.match == :smart or query.match == :prefer_exact)
|
304
|
+
result = query.resolve_for(self, true)
|
305
|
+
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
|
306
|
+
else
|
307
|
+
result = query.resolve_for(self)
|
308
|
+
end
|
309
|
+
|
310
|
+
if query.match == :one or query.match == :smart and result.size > 1
|
311
|
+
raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
|
312
|
+
end
|
313
|
+
if result.empty?
|
314
|
+
raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
|
315
|
+
end
|
316
|
+
result.first
|
317
|
+
end.tap(&:allow_reload!)
|
318
|
+
end
|
245
319
|
end
|
246
320
|
end
|
247
321
|
end
|
@@ -114,8 +114,8 @@ module Capybara
|
|
114
114
|
#
|
115
115
|
def assert_all_of_selectors(*args, &optional_filter_block)
|
116
116
|
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
117
|
-
selector = if args.first.is_a?(Symbol) then args.shift else
|
118
|
-
wait = options.fetch(:wait,
|
117
|
+
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
118
|
+
wait = options.fetch(:wait, session_options.default_max_wait_time)
|
119
119
|
synchronize(wait) do
|
120
120
|
args.each do |locator|
|
121
121
|
assert_selector(selector, locator, options, &optional_filter_block)
|
@@ -140,8 +140,8 @@ module Capybara
|
|
140
140
|
#
|
141
141
|
def assert_none_of_selectors(*args, &optional_filter_block)
|
142
142
|
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
143
|
-
selector = if args.first.is_a?(Symbol) then args.shift else
|
144
|
-
wait = options.fetch(:wait,
|
143
|
+
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
144
|
+
wait = options.fetch(:wait, session_options.default_max_wait_time)
|
145
145
|
synchronize(wait) do
|
146
146
|
args.each do |locator|
|
147
147
|
assert_no_selector(selector, locator, options, &optional_filter_block)
|
@@ -432,11 +432,12 @@ module Capybara
|
|
432
432
|
#
|
433
433
|
# page.has_select?('Language', with_options: ['English', 'German'])
|
434
434
|
#
|
435
|
-
# @param [String] locator
|
436
|
-
# @option options [Array] :options
|
437
|
-
# @option options [Array] :with_options
|
438
|
-
# @option options [String, Array] :selected
|
439
|
-
# @
|
435
|
+
# @param [String] locator The label, name or id of a select box
|
436
|
+
# @option options [Array] :options Options which should be contained in this select box
|
437
|
+
# @option options [Array] :with_options Partial set of options which should be contained in this select box
|
438
|
+
# @option options [String, Array] :selected Options which should be selected
|
439
|
+
# @option options [String, Array] :with_selected Partial set of options which should minimally be selected
|
440
|
+
# @return [Boolean] Whether it exists
|
440
441
|
#
|
441
442
|
def has_select?(locator=nil, options={}, &optional_filter_block)
|
442
443
|
locator, options = nil, locator if locator.is_a? Hash
|
@@ -680,6 +681,7 @@ module Capybara
|
|
680
681
|
private
|
681
682
|
|
682
683
|
def _verify_selector_result(query_args, optional_filter_block, &result_block)
|
684
|
+
_set_query_session_options(query_args)
|
683
685
|
query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
|
684
686
|
synchronize(query.wait) do
|
685
687
|
result = query.resolve_for(self)
|
@@ -689,6 +691,7 @@ module Capybara
|
|
689
691
|
end
|
690
692
|
|
691
693
|
def _verify_match_result(query_args, optional_filter_block, &result_block)
|
694
|
+
_set_query_session_options(query_args)
|
692
695
|
query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
|
693
696
|
synchronize(query.wait) do
|
694
697
|
result = query.resolve_for(self.query_scope)
|
@@ -698,6 +701,7 @@ module Capybara
|
|
698
701
|
end
|
699
702
|
|
700
703
|
def _verify_text(query_args)
|
704
|
+
_set_query_session_options(query_args)
|
701
705
|
query = Capybara::Queries::TextQuery.new(*query_args)
|
702
706
|
synchronize(query.wait) do
|
703
707
|
count = query.resolve_for(self)
|
@@ -706,6 +710,14 @@ module Capybara
|
|
706
710
|
return true
|
707
711
|
end
|
708
712
|
|
713
|
+
def _set_query_session_options(query_args)
|
714
|
+
if query_args.last.is_a? Hash
|
715
|
+
query_args.last[:session_options] = session_options
|
716
|
+
else
|
717
|
+
query_args.push(session_options: session_options)
|
718
|
+
end
|
719
|
+
query_args
|
720
|
+
end
|
709
721
|
end
|
710
722
|
end
|
711
723
|
end
|
data/lib/capybara/node/simple.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Capybara
|
3
|
+
module Queries
|
4
|
+
class AncestorQuery < MatchQuery
|
5
|
+
# @api private
|
6
|
+
def resolve_for(node, exact = nil)
|
7
|
+
@child_node = node
|
8
|
+
node.synchronize do
|
9
|
+
match_results = super(node.session.current_scope, exact)
|
10
|
+
node.all(:xpath, XPath.ancestor) do |el|
|
11
|
+
match_results.include?(el)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
desc = super
|
18
|
+
if @child_node && (child_query = @child_node.instance_variable_get(:@query))
|
19
|
+
desc += " that is an ancestor of #{child_query.description}"
|
20
|
+
end
|
21
|
+
desc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -6,13 +6,22 @@ module Capybara
|
|
6
6
|
COUNT_KEYS = [:count, :minimum, :maximum, :between]
|
7
7
|
|
8
8
|
attr_reader :options
|
9
|
+
attr_writer :session_options
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@session_options = options.delete(:session_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def session_options
|
16
|
+
@session_options || Capybara.session_options
|
17
|
+
end
|
9
18
|
|
10
19
|
def wait
|
11
|
-
self.class.wait(options)
|
20
|
+
self.class.wait(options, session_options.default_max_wait_time)
|
12
21
|
end
|
13
22
|
|
14
|
-
def self.wait(options)
|
15
|
-
options.fetch(:wait,
|
23
|
+
def self.wait(options, default=Capybara.default_max_wait_time)
|
24
|
+
options.fetch(:wait, default) || 0
|
16
25
|
end
|
17
26
|
|
18
27
|
##
|
@@ -6,28 +6,32 @@ module Capybara
|
|
6
6
|
module Queries
|
7
7
|
class CurrentPathQuery < BaseQuery
|
8
8
|
def initialize(expected_path, options = {})
|
9
|
+
super(options)
|
9
10
|
@expected_path = expected_path
|
11
|
+
warn "DEPRECATED: The :only_path option is deprecated in favor of the :ignore_query option" if options.has_key?(:only_path)
|
12
|
+
|
10
13
|
@options = {
|
11
|
-
url:
|
12
|
-
only_path: false
|
14
|
+
url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || "").hostname.nil?,
|
15
|
+
only_path: false,
|
16
|
+
ignore_query: false }.merge(options)
|
13
17
|
assert_valid_keys
|
14
18
|
end
|
15
19
|
|
16
20
|
def resolves_for?(session)
|
21
|
+
uri = ::Addressable::URI.parse(session.current_url)
|
22
|
+
uri.query = nil if uri && options[:ignore_query]
|
17
23
|
@actual_path = if options[:url]
|
18
|
-
|
24
|
+
uri.to_s
|
19
25
|
else
|
20
|
-
uri = ::Addressable::URI.parse(session.current_url)
|
21
|
-
|
22
26
|
if options[:only_path]
|
23
|
-
uri
|
27
|
+
uri && uri.path
|
24
28
|
else
|
25
|
-
uri
|
29
|
+
uri && uri.request_uri
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
29
33
|
if @expected_path.is_a? Regexp
|
30
|
-
@actual_path.match(@expected_path)
|
34
|
+
@actual_path.to_s.match(@expected_path)
|
31
35
|
else
|
32
36
|
::Addressable::URI.parse(@expected_path) == ::Addressable::URI.parse(@actual_path)
|
33
37
|
end
|
@@ -49,7 +53,7 @@ module Capybara
|
|
49
53
|
end
|
50
54
|
|
51
55
|
def valid_keys
|
52
|
-
[:wait, :url, :only_path]
|
56
|
+
[:wait, :url, :only_path, :ignore_query]
|
53
57
|
end
|
54
58
|
|
55
59
|
def assert_valid_keys
|
@@ -8,29 +8,31 @@ module Capybara
|
|
8
8
|
VALID_MATCH = [:first, :smart, :prefer_exact, :one]
|
9
9
|
|
10
10
|
def initialize(*args, &filter_block)
|
11
|
+
@resolved_node = nil
|
11
12
|
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
13
|
+
super(@options)
|
14
|
+
|
12
15
|
@filter_block = filter_block
|
13
16
|
|
14
17
|
if args[0].is_a?(Symbol)
|
15
18
|
@selector = Selector.all.fetch(args.shift) do |selector_type|
|
16
|
-
|
17
|
-
nil
|
19
|
+
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
18
20
|
end
|
19
21
|
@locator = args.shift
|
20
22
|
else
|
21
23
|
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
|
22
24
|
@locator = args.shift
|
23
25
|
end
|
24
|
-
@selector ||= Selector.all[
|
26
|
+
@selector ||= Selector.all[session_options.default_selector]
|
25
27
|
|
26
|
-
warn "Unused parameters passed to #{self.class.name} : #{args
|
28
|
+
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
27
29
|
|
28
30
|
# for compatibility with Capybara 2.0
|
29
|
-
if
|
31
|
+
if session_options.exact_options and @selector == Selector.all[:option]
|
30
32
|
@options[:exact] = true
|
31
33
|
end
|
32
34
|
|
33
|
-
@expression = @selector.call(@locator, @options)
|
35
|
+
@expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
|
34
36
|
|
35
37
|
warn_exact_usage
|
36
38
|
|
@@ -41,13 +43,17 @@ module Capybara
|
|
41
43
|
def label; selector.label or selector.name; end
|
42
44
|
|
43
45
|
def description
|
44
|
-
@description = String.new(
|
46
|
+
@description = String.new()
|
47
|
+
@description << "visible " if visible == :visible
|
48
|
+
@description << "non-visible " if visible == :hidden
|
49
|
+
@description << "#{label} #{locator.inspect}"
|
45
50
|
@description << " with#{" exact" if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
46
51
|
@description << " with exact text #{options[:exact_text]}" if options[:exact_text].is_a?(String)
|
47
52
|
@description << " with id #{options[:id]}" if options[:id]
|
48
|
-
@description << " with classes #{Array(options[:class]).join(',')}]" if options[:class]
|
53
|
+
@description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
49
54
|
@description << selector.description(options)
|
50
55
|
@description << " that also matches the custom filter block" if @filter_block
|
56
|
+
@description << " within #{@resolved_node.inspect}" if describe_within?
|
51
57
|
@description
|
52
58
|
end
|
53
59
|
|
@@ -79,7 +85,7 @@ module Capybara
|
|
79
85
|
when :hidden then return false if node.visible?
|
80
86
|
end
|
81
87
|
|
82
|
-
res =
|
88
|
+
res = node_filters.all? do |name, filter|
|
83
89
|
if options.has_key?(name)
|
84
90
|
filter.matches?(node, options[name])
|
85
91
|
elsif filter.default?
|
@@ -89,12 +95,20 @@ module Capybara
|
|
89
95
|
end
|
90
96
|
end
|
91
97
|
|
92
|
-
res &&=
|
98
|
+
res &&= if node.respond_to?(:session)
|
99
|
+
node.session.using_wait_time(0){ @filter_block.call(node) }
|
100
|
+
else
|
101
|
+
@filter_block.call(node)
|
102
|
+
end unless @filter_block.nil?
|
103
|
+
|
93
104
|
res
|
105
|
+
|
106
|
+
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
107
|
+
return false
|
94
108
|
end
|
95
109
|
|
96
110
|
def visible
|
97
|
-
case (vis = options.fetch(:visible){ @selector.default_visibility })
|
111
|
+
case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
|
98
112
|
when true then :visible
|
99
113
|
when false then :all
|
100
114
|
else vis
|
@@ -103,29 +117,31 @@ module Capybara
|
|
103
117
|
|
104
118
|
def exact?
|
105
119
|
return false if !supports_exact?
|
106
|
-
options.fetch(:exact,
|
120
|
+
options.fetch(:exact, session_options.exact)
|
107
121
|
end
|
108
122
|
|
109
123
|
def match
|
110
|
-
options.fetch(:match,
|
124
|
+
options.fetch(:match, session_options.match)
|
111
125
|
end
|
112
126
|
|
113
127
|
def xpath(exact=nil)
|
114
128
|
exact = self.exact? if exact.nil?
|
115
|
-
expr =
|
116
|
-
|
129
|
+
expr = apply_expression_filters(@expression)
|
130
|
+
expr = if expr.respond_to?(:to_xpath) and exact
|
131
|
+
expr.to_xpath(:exact)
|
117
132
|
else
|
118
|
-
|
133
|
+
expr.to_s
|
119
134
|
end
|
120
135
|
filtered_xpath(expr)
|
121
136
|
end
|
122
137
|
|
123
138
|
def css
|
124
|
-
filtered_css(@expression)
|
139
|
+
filtered_css(apply_expression_filters(@expression))
|
125
140
|
end
|
126
141
|
|
127
142
|
# @api private
|
128
143
|
def resolve_for(node, exact = nil)
|
144
|
+
@resolved_node = node
|
129
145
|
node.synchronize do
|
130
146
|
children = if selector.format == :css
|
131
147
|
node.find_css(self.css)
|
@@ -153,16 +169,22 @@ module Capybara
|
|
153
169
|
VALID_KEYS + custom_keys
|
154
170
|
end
|
155
171
|
|
156
|
-
def
|
172
|
+
def node_filters
|
157
173
|
if options.has_key?(:filter_set)
|
158
|
-
Capybara::Selector::FilterSet.all[options[:filter_set]].
|
174
|
+
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
159
175
|
else
|
160
|
-
@selector.
|
176
|
+
@selector.node_filters
|
161
177
|
end
|
162
178
|
end
|
163
179
|
|
180
|
+
def expression_filters
|
181
|
+
filters = @selector.expression_filters
|
182
|
+
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.has_key?(:filter_set)
|
183
|
+
filters
|
184
|
+
end
|
185
|
+
|
164
186
|
def custom_keys
|
165
|
-
@custom_keys ||=
|
187
|
+
@custom_keys ||= node_filters.keys + expression_filters.keys
|
166
188
|
end
|
167
189
|
|
168
190
|
def assert_valid_keys
|
@@ -198,14 +220,31 @@ module Capybara
|
|
198
220
|
expr
|
199
221
|
end
|
200
222
|
|
223
|
+
def apply_expression_filters(expr)
|
224
|
+
expression_filters.inject(expr) do |memo, (name, ef)|
|
225
|
+
if options.has_key?(name)
|
226
|
+
ef.apply_filter(memo, options[name])
|
227
|
+
elsif ef.default?
|
228
|
+
ef.apply_filter(memo, ef.default)
|
229
|
+
else
|
230
|
+
memo
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
201
235
|
def warn_exact_usage
|
202
236
|
if options.has_key?(:exact) && !supports_exact?
|
203
|
-
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression
|
237
|
+
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
204
238
|
end
|
205
239
|
end
|
206
240
|
|
207
241
|
def exact_text
|
208
|
-
options.fetch(:exact_text,
|
242
|
+
options.fetch(:exact_text, session_options.exact_text)
|
243
|
+
end
|
244
|
+
|
245
|
+
def describe_within?
|
246
|
+
@resolved_node && !(@resolved_node.is_a?(::Capybara::Node::Document) ||
|
247
|
+
(@resolved_node.is_a?(::Capybara::Node::Simple) && @resolved_node.path == '/'))
|
209
248
|
end
|
210
249
|
end
|
211
250
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Capybara
|
3
|
+
module Queries
|
4
|
+
class SiblingQuery < MatchQuery
|
5
|
+
# @api private
|
6
|
+
def resolve_for(node, exact = nil)
|
7
|
+
@sibling_node = node
|
8
|
+
node.synchronize do
|
9
|
+
match_results = super(node.session.current_scope, exact)
|
10
|
+
node.all(:xpath, XPath.preceding_sibling.union(XPath.following_sibling)) do |el|
|
11
|
+
match_results.include?(el)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
desc = super
|
18
|
+
if @sibling_node && (sibling_query = @sibling_node.instance_variable_get(:@query))
|
19
|
+
desc += " that is a sibling of #{sibling_query.description}"
|
20
|
+
end
|
21
|
+
desc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -5,13 +5,18 @@ module Capybara
|
|
5
5
|
class TextQuery < BaseQuery
|
6
6
|
def initialize(*args)
|
7
7
|
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
8
|
-
@type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
|
-
@
|
8
|
+
# @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
|
+
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
10
|
+
super(@options)
|
11
|
+
|
12
|
+
@type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
|
13
|
+
|
14
|
+
@expected_text = args.shift
|
10
15
|
unless @expected_text.is_a?(Regexp)
|
11
16
|
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
12
17
|
end
|
13
|
-
@options ||= {}
|
14
18
|
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
19
|
+
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
15
20
|
assert_valid_keys
|
16
21
|
end
|
17
22
|
|
@@ -40,7 +45,7 @@ module Capybara
|
|
40
45
|
private
|
41
46
|
|
42
47
|
def exact?
|
43
|
-
options.fetch(:exact,
|
48
|
+
options.fetch(:exact, session_options.exact_text)
|
44
49
|
end
|
45
50
|
|
46
51
|
def build_message(report_on_invisible)
|
@@ -65,7 +70,7 @@ module Capybara
|
|
65
70
|
invisible_text = text(@node, :all)
|
66
71
|
invisible_count = invisible_text.scan(@search_regexp).size
|
67
72
|
if invisible_count != @count
|
68
|
-
details_message << "
|
73
|
+
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
69
74
|
end
|
70
75
|
rescue
|
71
76
|
# An error getting the non-visible text (if element goes out of scope) should not affect the response
|
@@ -22,6 +22,11 @@ class Capybara::RackTest::Browser
|
|
22
22
|
process_and_follow_redirects(:get, path, attributes)
|
23
23
|
end
|
24
24
|
|
25
|
+
def refresh
|
26
|
+
reset_cache!
|
27
|
+
request(last_request.fullpath, last_request.env)
|
28
|
+
end
|
29
|
+
|
25
30
|
def submit(method, path, attributes)
|
26
31
|
path = request_path if not path or path.empty?
|
27
32
|
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
|
@@ -45,10 +50,13 @@ class Capybara::RackTest::Browser
|
|
45
50
|
def process(method, path, attributes = {}, env = {})
|
46
51
|
new_uri = URI.parse(path)
|
47
52
|
method.downcase! unless method.is_a? Symbol
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
if path.empty?
|
54
|
+
new_uri.path = request_path
|
55
|
+
else
|
56
|
+
new_uri.path = request_path if path.start_with?("?")
|
57
|
+
new_uri.path = "/" if new_uri.path.empty?
|
58
|
+
new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
|
59
|
+
end
|
52
60
|
new_uri.scheme ||= @current_scheme
|
53
61
|
new_uri.host ||= @current_host
|
54
62
|
new_uri.port ||= @current_port unless new_uri.default_port == @current_port
|
@@ -68,7 +76,7 @@ class Capybara::RackTest::Browser
|
|
68
76
|
end
|
69
77
|
|
70
78
|
def reset_host!
|
71
|
-
uri = URI.parse(
|
79
|
+
uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
|
72
80
|
@current_scheme = uri.scheme
|
73
81
|
@current_host = uri.host
|
74
82
|
@current_port = uri.port
|