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.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +26 -1
  3. data/README.md +12 -12
  4. data/lib/capybara.rb +13 -25
  5. data/lib/capybara/config.rb +11 -57
  6. data/lib/capybara/cucumber.rb +2 -3
  7. data/lib/capybara/driver/base.rb +5 -16
  8. data/lib/capybara/driver/node.rb +5 -4
  9. data/lib/capybara/dsl.rb +1 -0
  10. data/lib/capybara/helpers.rb +16 -28
  11. data/lib/capybara/minitest.rb +139 -138
  12. data/lib/capybara/minitest/spec.rb +15 -14
  13. data/lib/capybara/node/actions.rb +59 -81
  14. data/lib/capybara/node/base.rb +11 -18
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +8 -8
  17. data/lib/capybara/node/element.rb +30 -40
  18. data/lib/capybara/node/finders.rb +62 -70
  19. data/lib/capybara/node/matchers.rb +48 -71
  20. data/lib/capybara/node/simple.rb +11 -17
  21. data/lib/capybara/queries/ancestor_query.rb +4 -6
  22. data/lib/capybara/queries/base_query.rb +18 -17
  23. data/lib/capybara/queries/current_path_query.rb +8 -24
  24. data/lib/capybara/queries/match_query.rb +3 -7
  25. data/lib/capybara/queries/selector_query.rb +92 -95
  26. data/lib/capybara/queries/sibling_query.rb +4 -4
  27. data/lib/capybara/queries/text_query.rb +37 -34
  28. data/lib/capybara/queries/title_query.rb +8 -11
  29. data/lib/capybara/rack_test/browser.rb +15 -18
  30. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  31. data/lib/capybara/rack_test/driver.rb +6 -10
  32. data/lib/capybara/rack_test/form.rb +50 -40
  33. data/lib/capybara/rack_test/node.rb +70 -56
  34. data/lib/capybara/rails.rb +2 -6
  35. data/lib/capybara/result.rb +22 -22
  36. data/lib/capybara/rspec.rb +5 -10
  37. data/lib/capybara/rspec/compound.rb +5 -10
  38. data/lib/capybara/rspec/features.rb +17 -48
  39. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  40. data/lib/capybara/rspec/matchers.rb +70 -60
  41. data/lib/capybara/selector.rb +129 -117
  42. data/lib/capybara/selector/css.rb +6 -11
  43. data/lib/capybara/selector/filter.rb +1 -17
  44. data/lib/capybara/selector/filter_set.rb +17 -14
  45. data/lib/capybara/selector/filters/base.rb +7 -6
  46. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  47. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  48. data/lib/capybara/selector/selector.rb +27 -33
  49. data/lib/capybara/selenium/driver.rb +113 -127
  50. data/lib/capybara/selenium/node.rb +148 -113
  51. data/lib/capybara/server.rb +3 -2
  52. data/lib/capybara/session.rb +137 -223
  53. data/lib/capybara/session/config.rb +47 -67
  54. data/lib/capybara/session/matchers.rb +8 -7
  55. data/lib/capybara/spec/public/test.js +26 -4
  56. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  57. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  58. data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
  59. data/lib/capybara/spec/session/all_spec.rb +31 -18
  60. data/lib/capybara/spec/session/ancestor_spec.rb +2 -4
  61. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  62. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  63. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  64. data/lib/capybara/spec/session/assert_text.rb +18 -17
  65. data/lib/capybara/spec/session/assert_title.rb +1 -0
  66. data/lib/capybara/spec/session/attach_file_spec.rb +14 -13
  67. data/lib/capybara/spec/session/body_spec.rb +1 -0
  68. data/lib/capybara/spec/session/check_spec.rb +7 -6
  69. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  70. data/lib/capybara/spec/session/click_button_spec.rb +20 -28
  71. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  72. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  73. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  74. data/lib/capybara/spec/session/current_url_spec.rb +7 -6
  75. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  76. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  77. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  78. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  79. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  80. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
  81. data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
  82. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  83. data/lib/capybara/spec/session/fill_in_spec.rb +6 -5
  84. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  85. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  86. data/lib/capybara/spec/session/find_field_spec.rb +8 -14
  87. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  88. data/lib/capybara/spec/session/find_spec.rb +37 -31
  89. data/lib/capybara/spec/session/first_spec.rb +60 -33
  90. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  91. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  92. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  93. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  94. data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
  95. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  96. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  97. data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
  98. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  99. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  100. data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
  101. data/lib/capybara/spec/session/has_select_spec.rb +30 -29
  102. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  103. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  104. data/lib/capybara/spec/session/has_text_spec.rb +6 -5
  105. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  106. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  107. data/lib/capybara/spec/session/headers.rb +2 -1
  108. data/lib/capybara/spec/session/html_spec.rb +1 -0
  109. data/lib/capybara/spec/session/node_spec.rb +91 -56
  110. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  111. data/lib/capybara/spec/session/refresh_spec.rb +4 -2
  112. data/lib/capybara/spec/session/reset_session_spec.rb +1 -0
  113. data/lib/capybara/spec/session/response_code.rb +1 -0
  114. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  115. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  116. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  117. data/lib/capybara/spec/session/save_screenshot_spec.rb +1 -1
  118. data/lib/capybara/spec/session/select_spec.rb +20 -20
  119. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  120. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  121. data/lib/capybara/spec/session/text_spec.rb +1 -0
  122. data/lib/capybara/spec/session/title_spec.rb +1 -1
  123. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  124. data/lib/capybara/spec/session/unselect_spec.rb +6 -5
  125. data/lib/capybara/spec/session/visit_spec.rb +9 -3
  126. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  127. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  128. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  129. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  130. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  131. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  132. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  133. data/lib/capybara/spec/session/window/within_window_spec.rb +13 -68
  134. data/lib/capybara/spec/session/within_spec.rb +1 -0
  135. data/lib/capybara/spec/spec_helper.rb +26 -18
  136. data/lib/capybara/spec/test_app.rb +8 -9
  137. data/lib/capybara/spec/views/form.erb +1 -0
  138. data/lib/capybara/spec/views/with_html.erb +3 -1
  139. data/lib/capybara/spec/views/within_frames.erb +4 -1
  140. data/lib/capybara/version.rb +2 -1
  141. data/lib/capybara/window.rb +6 -10
  142. data/spec/basic_node_spec.rb +1 -0
  143. data/spec/capybara_spec.rb +9 -32
  144. data/spec/dsl_spec.rb +5 -13
  145. data/spec/filter_set_spec.rb +5 -4
  146. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  147. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  148. data/spec/minitest_spec.rb +4 -3
  149. data/spec/minitest_spec_spec.rb +3 -2
  150. data/spec/per_session_config_spec.rb +9 -8
  151. data/spec/rack_test_spec.rb +21 -20
  152. data/spec/result_spec.rb +17 -16
  153. data/spec/rspec/features_spec.rb +17 -14
  154. data/spec/rspec/scenarios_spec.rb +5 -7
  155. data/spec/rspec/shared_spec_matchers.rb +96 -99
  156. data/spec/rspec/views_spec.rb +2 -1
  157. data/spec/rspec_matchers_spec.rb +19 -2
  158. data/spec/rspec_spec.rb +11 -15
  159. data/spec/selector_spec.rb +5 -6
  160. data/spec/selenium_spec_chrome.rb +7 -4
  161. data/spec/selenium_spec_marionette.rb +26 -12
  162. data/spec/server_spec.rb +33 -33
  163. data/spec/session_spec.rb +2 -1
  164. data/spec/shared_selenium_session.rb +27 -21
  165. data/spec/spec_helper.rb +2 -5
  166. metadata +66 -87
  167. data/lib/capybara/query.rb +0 -7
  168. data/spec/selenium_spec_firefox.rb +0 -68
@@ -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(type=nil)
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 'input' == tag_name and 'checkbox' == native[:type] and 'checked' == attr_name
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(radio checkbox).include?(native[:type])
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
- #check size because oldest supported nokogiri doesnt support xpath boolean() function
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
- #no need for an xpath if only checking the current element
110
- !(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w(script head).include?(tag_name))
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(seconds=nil)
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
- if native.respond_to? :title
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) do |el|
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
- if @child_node && (child_query = @child_node.instance_variable_get(:@query))
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 = [:count, :minimum, :maximum, :between]
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
- if COUNT_KEYS.any? { |k| options.has_key? k }
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) if options[: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] && !(options[:between] === count)
54
- return true
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
- private
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 = String.new()
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
- unless invalid_keys.empty?
88
- invalid_names = invalid_keys.map(&:inspect).join(", ")
89
- valid_names = valid_keys.map(&:inspect).join(", ")
90
- raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
91
- end
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
- only_path: false,
16
- ignore_query: false }.merge(options)
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 = if options[:url]
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
- private
39
+ private
49
40
 
50
41
  def failure_message_helper(negated = '')
51
- verb = (@expected_path.is_a?(Regexp))? 'match' : 'equal'
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
- [:wait, :url, :only_path, :ignore_query]
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
- if options.has_key?(:visible)
6
- super
7
- else
8
- :all
9
- end
5
+ options.key?(:visible) ? super : :all
10
6
  end
11
7
 
12
- private
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 + [:text, :id, :class, :visible, :exact, :exact_text, :match, :wait, :filter_set]
8
- VALID_MATCH = [:first, :smart, :prefer_exact, :one]
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 = if args.last.is_a?(Hash) then args.pop.dup else {} end
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
- if args[0].is_a?(Symbol)
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 or selector.name; end
31
+ def label; selector.label || selector.name; end
44
32
 
45
33
  def description
46
- @description = String.new()
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
- regexp = if options[:text].is_a?(Regexp)
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
- when :visible then return false unless node.visible?
85
- when :hidden then return false if node.visible?
53
+ when :visible then return false unless node.visible?
54
+ when :hidden then return false if node.visible?
86
55
  end
87
56
 
88
- res = node_filters.all? do |name, filter|
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
- when true then :visible
113
- when false then :all
114
- else vis
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
- return false if !supports_exact?
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 = self.exact? if exact.nil?
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) and exact
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(self.css)
94
+ node.find_css(css)
148
95
  else
149
- node.find_xpath(self.xpath(exact))
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
- private
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.has_key?(:filter_set)
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.has_key?(:filter_set)
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.has_key?(:id) || options.has_key?(:class)
176
+ if options.key?(:id) || options.key?(:class)
199
177
  expr = "(#{expr})"
200
- expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.has_key?(:id) && !custom_keys.include?(:id)
201
- if options.has_key?(:class) && !custom_keys.include?(:class)
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
- "contains(concat(' ',normalize-space(@class),' '),' #{klass} ')"
204
- end.join(" and ")
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.has_key?(:id) || options.has_key?(:class)
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
- sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.has_key?(:id) && !custom_keys.include?(:id)
216
- sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}"}.join if options.has_key?(:class) && !custom_keys.include?(:class)
217
- sel
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.has_key?(name)
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
- if options.has_key?(:exact) && !supports_exact?
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."
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