capybara 2.18.0 → 3.0.0.rc1
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/History.md +26 -1
- data/README.md +12 -12
- data/lib/capybara.rb +13 -25
- data/lib/capybara/config.rb +11 -57
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +5 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +16 -28
- data/lib/capybara/minitest.rb +139 -138
- data/lib/capybara/minitest/spec.rb +15 -14
- data/lib/capybara/node/actions.rb +59 -81
- data/lib/capybara/node/base.rb +11 -18
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +8 -8
- data/lib/capybara/node/element.rb +30 -40
- data/lib/capybara/node/finders.rb +62 -70
- data/lib/capybara/node/matchers.rb +48 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +4 -6
- data/lib/capybara/queries/base_query.rb +18 -17
- data/lib/capybara/queries/current_path_query.rb +8 -24
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +92 -95
- data/lib/capybara/queries/sibling_query.rb +4 -4
- data/lib/capybara/queries/text_query.rb +37 -34
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +15 -18
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +6 -10
- data/lib/capybara/rack_test/form.rb +50 -40
- data/lib/capybara/rack_test/node.rb +70 -56
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/rspec/compound.rb +5 -10
- data/lib/capybara/rspec/features.rb +17 -48
- data/lib/capybara/rspec/matcher_proxies.rb +31 -15
- data/lib/capybara/rspec/matchers.rb +70 -60
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +17 -14
- data/lib/capybara/selector/filters/base.rb +7 -6
- data/lib/capybara/selector/filters/expression_filter.rb +6 -23
- data/lib/capybara/selector/filters/node_filter.rb +2 -12
- data/lib/capybara/selector/selector.rb +27 -33
- data/lib/capybara/selenium/driver.rb +113 -127
- data/lib/capybara/selenium/node.rb +148 -113
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session.rb +137 -223
- data/lib/capybara/session/config.rb +47 -67
- data/lib/capybara/session/matchers.rb +8 -7
- data/lib/capybara/spec/public/test.js +26 -4
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/all_spec.rb +31 -18
- data/lib/capybara/spec/session/ancestor_spec.rb +2 -4
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
- data/lib/capybara/spec/session/assert_current_path.rb +12 -11
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +18 -17
- data/lib/capybara/spec/session/assert_title.rb +1 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +14 -13
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +7 -6
- data/lib/capybara/spec/session/choose_spec.rb +5 -4
- data/lib/capybara/spec/session/click_button_spec.rb +20 -28
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
- data/lib/capybara/spec/session/click_link_spec.rb +8 -7
- data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
- data/lib/capybara/spec/session/current_url_spec.rb +7 -6
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
- data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -5
- data/lib/capybara/spec/session/find_button_spec.rb +4 -3
- data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
- data/lib/capybara/spec/session/find_field_spec.rb +8 -14
- data/lib/capybara/spec/session/find_link_spec.rb +6 -5
- data/lib/capybara/spec/session/find_spec.rb +37 -31
- data/lib/capybara/spec/session/first_spec.rb +60 -33
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
- data/lib/capybara/spec/session/has_button_spec.rb +2 -1
- data/lib/capybara/spec/session/has_css_spec.rb +3 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +1 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/has_select_spec.rb +30 -29
- data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
- data/lib/capybara/spec/session/has_table_spec.rb +2 -1
- data/lib/capybara/spec/session/has_text_spec.rb +6 -5
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +91 -56
- data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
- data/lib/capybara/spec/session/refresh_spec.rb +4 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +1 -0
- data/lib/capybara/spec/session/response_code.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
- data/lib/capybara/spec/session/save_page_spec.rb +1 -17
- data/lib/capybara/spec/session/save_screenshot_spec.rb +1 -1
- data/lib/capybara/spec/session/select_spec.rb +20 -20
- data/lib/capybara/spec/session/selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +1 -1
- data/lib/capybara/spec/session/text_spec.rb +1 -0
- data/lib/capybara/spec/session/title_spec.rb +1 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
- data/lib/capybara/spec/session/unselect_spec.rb +6 -5
- data/lib/capybara/spec/session/visit_spec.rb +9 -3
- data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +12 -12
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
- data/lib/capybara/spec/session/window/within_window_spec.rb +13 -68
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +26 -18
- data/lib/capybara/spec/test_app.rb +8 -9
- data/lib/capybara/spec/views/form.erb +1 -0
- data/lib/capybara/spec/views/with_html.erb +3 -1
- data/lib/capybara/spec/views/within_frames.erb +4 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +6 -10
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +9 -32
- data/spec/dsl_spec.rb +5 -13
- data/spec/filter_set_spec.rb +5 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
- data/spec/minitest_spec.rb +4 -3
- data/spec/minitest_spec_spec.rb +3 -2
- data/spec/per_session_config_spec.rb +9 -8
- data/spec/rack_test_spec.rb +21 -20
- data/spec/result_spec.rb +17 -16
- data/spec/rspec/features_spec.rb +17 -14
- data/spec/rspec/scenarios_spec.rb +5 -7
- data/spec/rspec/shared_spec_matchers.rb +96 -99
- data/spec/rspec/views_spec.rb +2 -1
- data/spec/rspec_matchers_spec.rb +19 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +7 -4
- data/spec/selenium_spec_marionette.rb +26 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +27 -21
- data/spec/spec_helper.rb +2 -5
- metadata +66 -87
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
data/lib/capybara/node/simple.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Node
|
4
|
-
|
5
5
|
##
|
6
6
|
#
|
7
7
|
# A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
|
@@ -28,7 +28,7 @@ module Capybara
|
|
28
28
|
#
|
29
29
|
# @return [String] The text of the element
|
30
30
|
#
|
31
|
-
def text(
|
31
|
+
def text(_type = nil)
|
32
32
|
native.text
|
33
33
|
end
|
34
34
|
|
@@ -45,7 +45,7 @@ module Capybara
|
|
45
45
|
attr_name = name.to_s
|
46
46
|
if attr_name == 'value'
|
47
47
|
value
|
48
|
-
elsif
|
48
|
+
elsif tag_name == 'input' and native[:type] == 'checkbox' and attr_name == 'checked'
|
49
49
|
native['checked'] == 'checked'
|
50
50
|
else
|
51
51
|
native[attr_name]
|
@@ -79,12 +79,12 @@ module Capybara
|
|
79
79
|
native['_capybara_raw_value']
|
80
80
|
elsif tag_name == 'select'
|
81
81
|
if native['multiple'] == 'multiple'
|
82
|
-
native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content
|
82
|
+
native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
|
83
83
|
else
|
84
84
|
option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
|
85
85
|
option[:value] || option.content if option
|
86
86
|
end
|
87
|
-
elsif tag_name == 'input' && %w
|
87
|
+
elsif tag_name == 'input' && %w[radio checkbox].include?(native[:type])
|
88
88
|
native[:value] || 'on'
|
89
89
|
else
|
90
90
|
native[:value]
|
@@ -100,14 +100,13 @@ module Capybara
|
|
100
100
|
# @return [Boolean] Whether the element is visible
|
101
101
|
#
|
102
102
|
def visible?(check_ancestors = true)
|
103
|
-
return false if (tag_name == 'input') && (native[:type]=="hidden")
|
103
|
+
return false if (tag_name == 'input') && (native[:type] == "hidden")
|
104
104
|
|
105
105
|
if check_ancestors
|
106
|
-
|
107
|
-
native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head']").size() == 0
|
106
|
+
!native.xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
|
108
107
|
else
|
109
|
-
#
|
110
|
-
!(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w
|
108
|
+
# No need for an xpath if only checking the current element
|
109
|
+
!(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w[script head].include?(tag_name))
|
111
110
|
end
|
112
111
|
end
|
113
112
|
|
@@ -140,7 +139,7 @@ module Capybara
|
|
140
139
|
native.has_attribute?('selected')
|
141
140
|
end
|
142
141
|
|
143
|
-
def synchronize(
|
142
|
+
def synchronize(_seconds = nil)
|
144
143
|
yield # simple nodes don't need to wait
|
145
144
|
end
|
146
145
|
|
@@ -152,12 +151,7 @@ module Capybara
|
|
152
151
|
#
|
153
152
|
# @return [String] The title of the document
|
154
153
|
def title
|
155
|
-
|
156
|
-
native.title
|
157
|
-
else
|
158
|
-
#old versions of nokogiri don't have #title - remove in 3.0
|
159
|
-
native.xpath('/html/head/title | /html/title').first.text
|
160
|
-
end
|
154
|
+
native.title
|
161
155
|
end
|
162
156
|
|
163
157
|
def inspect
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Queries
|
4
5
|
class AncestorQuery < MatchQuery
|
@@ -7,17 +8,14 @@ module Capybara
|
|
7
8
|
@child_node = node
|
8
9
|
node.synchronize do
|
9
10
|
match_results = super(node.session.current_scope, exact)
|
10
|
-
node.all(:xpath, XPath.ancestor)
|
11
|
-
match_results.include?(el)
|
12
|
-
end
|
11
|
+
node.all(:xpath, XPath.ancestor) { |el| match_results.include?(el) }
|
13
12
|
end
|
14
13
|
end
|
15
14
|
|
16
15
|
def description
|
16
|
+
child_query = @child_node && @child_node.instance_variable_get(:@query)
|
17
17
|
desc = super
|
18
|
-
|
19
|
-
desc += " that is an ancestor of #{child_query.description}"
|
20
|
-
end
|
18
|
+
desc += " that is an ancestor of #{child_query.description}" if child_query
|
21
19
|
desc
|
22
20
|
end
|
23
21
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
# @api private
|
4
5
|
module Queries
|
5
6
|
class BaseQuery
|
6
|
-
COUNT_KEYS = [
|
7
|
+
COUNT_KEYS = %i[count minimum maximum between].freeze
|
7
8
|
|
8
9
|
attr_reader :options
|
9
10
|
attr_writer :session_options
|
@@ -20,7 +21,7 @@ module Capybara
|
|
20
21
|
self.class.wait(options, session_options.default_max_wait_time)
|
21
22
|
end
|
22
23
|
|
23
|
-
def self.wait(options, default=Capybara.default_max_wait_time)
|
24
|
+
def self.wait(options, default = Capybara.default_max_wait_time)
|
24
25
|
options.fetch(:wait, default) || 0
|
25
26
|
end
|
26
27
|
|
@@ -30,11 +31,7 @@ module Capybara
|
|
30
31
|
# Returns false if query does not have any count options specified.
|
31
32
|
#
|
32
33
|
def expects_none?
|
33
|
-
|
34
|
-
matches_count?(0)
|
35
|
-
else
|
36
|
-
false
|
37
|
-
end
|
34
|
+
count_specified? ? matches_count?(0) : false
|
38
35
|
end
|
39
36
|
|
40
37
|
##
|
@@ -47,11 +44,11 @@ module Capybara
|
|
47
44
|
# @param [Integer] count The actual number. Should be coercible via Integer()
|
48
45
|
#
|
49
46
|
def matches_count?(count)
|
50
|
-
return (Integer(options[:count]) == count)
|
47
|
+
return (Integer(options[:count]) == count) if options[:count]
|
51
48
|
return false if options[:maximum] && (Integer(options[:maximum]) < count)
|
52
49
|
return false if options[:minimum] && (Integer(options[:minimum]) > count)
|
53
|
-
return false if options[:between] && !
|
54
|
-
|
50
|
+
return false if options[:between] && !options[:between].include?(count)
|
51
|
+
true
|
55
52
|
end
|
56
53
|
|
57
54
|
##
|
@@ -66,10 +63,14 @@ module Capybara
|
|
66
63
|
String.new("expected not to find #{description}") << count_message
|
67
64
|
end
|
68
65
|
|
69
|
-
|
66
|
+
private
|
67
|
+
|
68
|
+
def count_specified?
|
69
|
+
COUNT_KEYS.any? { |k| options.key? k }
|
70
|
+
end
|
70
71
|
|
71
72
|
def count_message
|
72
|
-
message =
|
73
|
+
message = "".dup
|
73
74
|
if options[:count]
|
74
75
|
message << " #{options[:count]} #{Capybara::Helpers.declension('time', 'times', options[:count])}"
|
75
76
|
elsif options[:between]
|
@@ -84,11 +85,11 @@ module Capybara
|
|
84
85
|
|
85
86
|
def assert_valid_keys
|
86
87
|
invalid_keys = @options.keys - valid_keys
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
return if invalid_keys.empty?
|
89
|
+
|
90
|
+
invalid_names = invalid_keys.map(&:inspect).join(", ")
|
91
|
+
valid_names = valid_keys.map(&:inspect).join(", ")
|
92
|
+
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
92
93
|
end
|
93
94
|
end
|
94
95
|
end
|
@@ -1,34 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'addressable/uri'
|
3
4
|
|
4
5
|
module Capybara
|
5
6
|
# @api private
|
6
7
|
module Queries
|
7
8
|
class CurrentPathQuery < BaseQuery
|
8
|
-
def initialize(expected_path, options
|
9
|
+
def initialize(expected_path, **options)
|
9
10
|
super(options)
|
10
11
|
@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
|
-
|
13
12
|
@options = {
|
14
13
|
url: !@expected_path.is_a?(Regexp) && !::Addressable::URI.parse(@expected_path || "").hostname.nil?,
|
15
|
-
|
16
|
-
|
14
|
+
ignore_query: false
|
15
|
+
}.merge(options)
|
17
16
|
assert_valid_keys
|
18
17
|
end
|
19
18
|
|
20
19
|
def resolves_for?(session)
|
21
20
|
uri = ::Addressable::URI.parse(session.current_url)
|
22
21
|
uri.query = nil if uri && options[:ignore_query]
|
23
|
-
@actual_path =
|
24
|
-
uri.to_s
|
25
|
-
else
|
26
|
-
if options[:only_path]
|
27
|
-
uri && uri.path
|
28
|
-
else
|
29
|
-
uri && uri.request_uri
|
30
|
-
end
|
31
|
-
end
|
22
|
+
@actual_path = options[:url] ? uri.to_s : uri && uri.request_uri
|
32
23
|
|
33
24
|
if @expected_path.is_a? Regexp
|
34
25
|
@actual_path.to_s.match(@expected_path)
|
@@ -45,22 +36,15 @@ module Capybara
|
|
45
36
|
failure_message_helper(' not')
|
46
37
|
end
|
47
38
|
|
48
|
-
|
39
|
+
private
|
49
40
|
|
50
41
|
def failure_message_helper(negated = '')
|
51
|
-
verb =
|
42
|
+
verb = @expected_path.is_a?(Regexp) ? 'match' : 'equal'
|
52
43
|
"expected #{@actual_path.inspect}#{negated} to #{verb} #{@expected_path.inspect}"
|
53
44
|
end
|
54
45
|
|
55
46
|
def valid_keys
|
56
|
-
[
|
57
|
-
end
|
58
|
-
|
59
|
-
def assert_valid_keys
|
60
|
-
super
|
61
|
-
if options[:url] && options[:only_path]
|
62
|
-
raise ArgumentError, "the :url and :only_path options cannot both be true"
|
63
|
-
end
|
47
|
+
%i[wait url ignore_query]
|
64
48
|
end
|
65
49
|
end
|
66
50
|
end
|
@@ -2,18 +2,14 @@ module Capybara
|
|
2
2
|
module Queries
|
3
3
|
class MatchQuery < Capybara::Queries::SelectorQuery
|
4
4
|
def visible
|
5
|
-
|
6
|
-
super
|
7
|
-
else
|
8
|
-
:all
|
9
|
-
end
|
5
|
+
options.key?(:visible) ? super : :all
|
10
6
|
end
|
11
7
|
|
12
|
-
|
8
|
+
private
|
13
9
|
|
14
10
|
def valid_keys
|
15
11
|
super - COUNT_KEYS
|
16
12
|
end
|
17
13
|
end
|
18
14
|
end
|
19
|
-
end
|
15
|
+
end
|
@@ -1,36 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Queries
|
4
5
|
class SelectorQuery < Queries::BaseQuery
|
5
6
|
attr_accessor :selector, :locator, :options, :expression, :find, :negative
|
6
7
|
|
7
|
-
VALID_KEYS = COUNT_KEYS + [
|
8
|
-
VALID_MATCH = [
|
8
|
+
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
|
9
|
+
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
9
10
|
|
10
|
-
def initialize(*args, &filter_block)
|
11
|
+
def initialize(*args, session_options:, **options, &filter_block)
|
11
12
|
@resolved_node = nil
|
12
|
-
@options =
|
13
|
+
@options = options.dup
|
13
14
|
super(@options)
|
15
|
+
self.session_options = session_options
|
14
16
|
|
17
|
+
@selector = find_selector(args[0].is_a?(Symbol) ? args.shift : args[0])
|
18
|
+
@locator = args.shift
|
15
19
|
@filter_block = filter_block
|
16
20
|
|
17
|
-
|
18
|
-
@selector = Selector.all.fetch(args.shift) do |selector_type|
|
19
|
-
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
20
|
-
end
|
21
|
-
@locator = args.shift
|
22
|
-
else
|
23
|
-
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
|
24
|
-
@locator = args.shift
|
25
|
-
end
|
26
|
-
@selector ||= Selector.all[session_options.default_selector]
|
27
|
-
|
28
|
-
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
29
|
-
|
30
|
-
# for compatibility with Capybara 2.0
|
31
|
-
if session_options.exact_options and @selector == Selector.all[:option]
|
32
|
-
@options[:exact] = true
|
33
|
-
end
|
21
|
+
raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
34
22
|
|
35
23
|
@expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
|
36
24
|
|
@@ -40,10 +28,10 @@ module Capybara
|
|
40
28
|
end
|
41
29
|
|
42
30
|
def name; selector.name; end
|
43
|
-
def label; selector.label
|
31
|
+
def label; selector.label || selector.name; end
|
44
32
|
|
45
33
|
def description
|
46
|
-
@description =
|
34
|
+
@description = "".dup
|
47
35
|
@description << "visible " if visible == :visible
|
48
36
|
@description << "non-visible " if visible == :hidden
|
49
37
|
@description << "#{label} #{locator.inspect}"
|
@@ -58,80 +46,39 @@ module Capybara
|
|
58
46
|
end
|
59
47
|
|
60
48
|
def matches_filters?(node)
|
61
|
-
if options[:text]
|
62
|
-
|
63
|
-
options[:text]
|
64
|
-
else
|
65
|
-
if exact_text == true
|
66
|
-
/\A#{Regexp.escape(options[:text].to_s)}\z/
|
67
|
-
else
|
68
|
-
Regexp.escape(options[:text].to_s)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
text_visible = visible
|
72
|
-
text_visible = :all if text_visible == :hidden
|
73
|
-
return false if not node.text(text_visible).match(regexp)
|
74
|
-
end
|
75
|
-
|
76
|
-
if exact_text.is_a?(String)
|
77
|
-
regexp = /\A#{Regexp.escape(options[:exact_text])}\z/
|
78
|
-
text_visible = visible
|
79
|
-
text_visible = :all if text_visible == :hidden
|
80
|
-
return false if not node.text(text_visible).match(regexp)
|
81
|
-
end
|
49
|
+
return false if options[:text] && !matches_text_filter(node, options[:text])
|
50
|
+
return false if exact_text.is_a?(String) && !matches_exact_text_filter(node, exact_text)
|
82
51
|
|
83
52
|
case visible
|
84
|
-
|
85
|
-
|
53
|
+
when :visible then return false unless node.visible?
|
54
|
+
when :hidden then return false if node.visible?
|
86
55
|
end
|
87
56
|
|
88
|
-
|
89
|
-
if options.has_key?(name)
|
90
|
-
filter.matches?(node, options[name])
|
91
|
-
elsif filter.default?
|
92
|
-
filter.matches?(node, filter.default)
|
93
|
-
else
|
94
|
-
true
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
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
|
-
|
104
|
-
res
|
105
|
-
|
57
|
+
matches_node_filters?(node) && matches_filter_block?(node)
|
106
58
|
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
107
59
|
return false
|
108
60
|
end
|
109
61
|
|
110
62
|
def visible
|
111
|
-
case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
|
112
|
-
|
113
|
-
|
114
|
-
|
63
|
+
case (vis = options.fetch(:visible) { @selector.default_visibility(session_options.ignore_hidden_elements) })
|
64
|
+
when true then :visible
|
65
|
+
when false then :all
|
66
|
+
else vis
|
115
67
|
end
|
116
68
|
end
|
117
69
|
|
118
70
|
def exact?
|
119
|
-
|
120
|
-
options.fetch(:exact, session_options.exact)
|
71
|
+
supports_exact? ? options.fetch(:exact, session_options.exact) : false
|
121
72
|
end
|
122
73
|
|
123
74
|
def match
|
124
75
|
options.fetch(:match, session_options.match)
|
125
76
|
end
|
126
77
|
|
127
|
-
def xpath(exact=nil)
|
128
|
-
exact =
|
78
|
+
def xpath(exact = nil)
|
79
|
+
exact = exact? if exact.nil?
|
129
80
|
expr = apply_expression_filters(@expression)
|
130
|
-
expr = if expr.respond_to?(:to_xpath)
|
131
|
-
expr.to_xpath(:exact)
|
132
|
-
else
|
133
|
-
expr.to_s
|
134
|
-
end
|
81
|
+
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
135
82
|
filtered_xpath(expr)
|
136
83
|
end
|
137
84
|
|
@@ -144,9 +91,9 @@ module Capybara
|
|
144
91
|
@resolved_node = node
|
145
92
|
node.synchronize do
|
146
93
|
children = if selector.format == :css
|
147
|
-
node.find_css(
|
94
|
+
node.find_css(css)
|
148
95
|
else
|
149
|
-
node.find_xpath(
|
96
|
+
node.find_xpath(xpath(exact))
|
150
97
|
end.map do |child|
|
151
98
|
if node.is_a?(Capybara::Node::Base)
|
152
99
|
Capybara::Node::Element.new(node.session, child, node, self)
|
@@ -163,14 +110,45 @@ module Capybara
|
|
163
110
|
@expression.respond_to? :to_xpath
|
164
111
|
end
|
165
112
|
|
166
|
-
|
113
|
+
private
|
114
|
+
|
115
|
+
def find_selector(locator)
|
116
|
+
selector = if locator.is_a?(Symbol)
|
117
|
+
Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
118
|
+
else
|
119
|
+
Selector.all.values.find { |s| s.match?(locator) }
|
120
|
+
end
|
121
|
+
selector || Selector.all[session_options.default_selector]
|
122
|
+
end
|
167
123
|
|
168
124
|
def valid_keys
|
169
125
|
VALID_KEYS + custom_keys
|
170
126
|
end
|
171
127
|
|
128
|
+
def matches_node_filters?(node)
|
129
|
+
node_filters.all? do |name, filter|
|
130
|
+
if options.key?(name)
|
131
|
+
filter.matches?(node, options[name])
|
132
|
+
elsif filter.default?
|
133
|
+
filter.matches?(node, filter.default)
|
134
|
+
else
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def matches_filter_block?(node)
|
141
|
+
return true unless @filter_block
|
142
|
+
|
143
|
+
if node.respond_to?(:session)
|
144
|
+
node.session.using_wait_time(0) { @filter_block.call(node) }
|
145
|
+
else
|
146
|
+
@filter_block.call(node)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
172
150
|
def node_filters
|
173
|
-
if options.
|
151
|
+
if options.key?(:filter_set)
|
174
152
|
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
175
153
|
else
|
176
154
|
@selector.node_filters
|
@@ -179,7 +157,7 @@ module Capybara
|
|
179
157
|
|
180
158
|
def expression_filters
|
181
159
|
filters = @selector.expression_filters
|
182
|
-
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.
|
160
|
+
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.key?(:filter_set)
|
183
161
|
filters
|
184
162
|
end
|
185
163
|
|
@@ -195,13 +173,13 @@ module Capybara
|
|
195
173
|
end
|
196
174
|
|
197
175
|
def filtered_xpath(expr)
|
198
|
-
if options.
|
176
|
+
if options.key?(:id) || options.key?(:class)
|
199
177
|
expr = "(#{expr})"
|
200
|
-
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.
|
201
|
-
if options.
|
178
|
+
expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.key?(:id) && !custom_keys.include?(:id)
|
179
|
+
if options.key?(:class) && !custom_keys.include?(:class)
|
202
180
|
class_xpath = Array(options[:class]).map do |klass|
|
203
|
-
|
204
|
-
end.
|
181
|
+
XPath.attr(:class).contains_word(klass)
|
182
|
+
end.reduce(:&)
|
205
183
|
expr = "#{expr}[#{class_xpath}]"
|
206
184
|
end
|
207
185
|
end
|
@@ -209,12 +187,12 @@ module Capybara
|
|
209
187
|
end
|
210
188
|
|
211
189
|
def filtered_css(expr)
|
212
|
-
if options.
|
190
|
+
if options.key?(:id) || options.key?(:class)
|
213
191
|
css_selectors = expr.split(',').map(&:rstrip)
|
214
192
|
expr = css_selectors.map do |sel|
|
215
|
-
|
216
|
-
|
217
|
-
|
193
|
+
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.key?(:id) && !custom_keys.include?(:id)
|
194
|
+
sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}" }.join if options.key?(:class) && !custom_keys.include?(:class)
|
195
|
+
sel
|
218
196
|
end.join(", ")
|
219
197
|
end
|
220
198
|
expr
|
@@ -222,7 +200,7 @@ module Capybara
|
|
222
200
|
|
223
201
|
def apply_expression_filters(expr)
|
224
202
|
expression_filters.inject(expr) do |memo, (name, ef)|
|
225
|
-
if options.
|
203
|
+
if options.key?(name)
|
226
204
|
ef.apply_filter(memo, options[name])
|
227
205
|
elsif ef.default?
|
228
206
|
ef.apply_filter(memo, ef.default)
|
@@ -233,9 +211,8 @@ module Capybara
|
|
233
211
|
end
|
234
212
|
|
235
213
|
def warn_exact_usage
|
236
|
-
|
237
|
-
|
238
|
-
end
|
214
|
+
return unless options.key?(:exact) && !supports_exact?
|
215
|
+
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
|
239
216
|
end
|
240
217
|
|
241
218
|
def exact_text
|
@@ -246,6 +223,26 @@ module Capybara
|
|
246
223
|
@resolved_node && !(@resolved_node.is_a?(::Capybara::Node::Document) ||
|
247
224
|
(@resolved_node.is_a?(::Capybara::Node::Simple) && @resolved_node.path == '/'))
|
248
225
|
end
|
226
|
+
|
227
|
+
def matches_text_filter(node, text_option)
|
228
|
+
regexp = if text_option.is_a?(Regexp)
|
229
|
+
text_option
|
230
|
+
elsif exact_text == true
|
231
|
+
/\A#{Regexp.escape(text_option.to_s)}\z/
|
232
|
+
else
|
233
|
+
Regexp.escape(text_option.to_s)
|
234
|
+
end
|
235
|
+
text_visible = visible
|
236
|
+
text_visible = :all if text_visible == :hidden
|
237
|
+
node.text(text_visible).match(regexp)
|
238
|
+
end
|
239
|
+
|
240
|
+
def matches_exact_text_filter(node, exact_text_option)
|
241
|
+
regexp = /\A#{Regexp.escape(exact_text_option)}\z/
|
242
|
+
text_visible = visible
|
243
|
+
text_visible = :all if text_visible == :hidden
|
244
|
+
node.text(text_visible).match(regexp)
|
245
|
+
end
|
249
246
|
end
|
250
247
|
end
|
251
248
|
end
|