capybara 2.13.0 → 2.18.0
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 +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
|