capybara 2.18.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +55 -1
  3. data/README.md +18 -17
  4. data/lib/capybara/config.rb +11 -58
  5. data/lib/capybara/cucumber.rb +2 -3
  6. data/lib/capybara/driver/base.rb +15 -16
  7. data/lib/capybara/driver/node.rb +5 -4
  8. data/lib/capybara/dsl.rb +1 -0
  9. data/lib/capybara/helpers.rb +19 -29
  10. data/lib/capybara/minitest/spec.rb +15 -14
  11. data/lib/capybara/minitest.rb +139 -138
  12. data/lib/capybara/node/actions.rb +60 -81
  13. data/lib/capybara/node/base.rb +11 -18
  14. data/lib/capybara/node/document.rb +2 -2
  15. data/lib/capybara/node/document_matchers.rb +8 -8
  16. data/lib/capybara/node/element.rb +30 -40
  17. data/lib/capybara/node/finders.rb +62 -70
  18. data/lib/capybara/node/matchers.rb +50 -71
  19. data/lib/capybara/node/simple.rb +11 -17
  20. data/lib/capybara/queries/ancestor_query.rb +11 -7
  21. data/lib/capybara/queries/base_query.rb +22 -18
  22. data/lib/capybara/queries/current_path_query.rb +8 -24
  23. data/lib/capybara/queries/match_query.rb +3 -7
  24. data/lib/capybara/queries/selector_query.rb +92 -95
  25. data/lib/capybara/queries/sibling_query.rb +4 -4
  26. data/lib/capybara/queries/text_query.rb +35 -35
  27. data/lib/capybara/queries/title_query.rb +8 -11
  28. data/lib/capybara/rack_test/browser.rb +15 -18
  29. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  30. data/lib/capybara/rack_test/driver.rb +6 -10
  31. data/lib/capybara/rack_test/form.rb +50 -40
  32. data/lib/capybara/rack_test/node.rb +93 -63
  33. data/lib/capybara/rails.rb +2 -6
  34. data/lib/capybara/result.rb +22 -22
  35. data/lib/capybara/rspec/compound.rb +5 -10
  36. data/lib/capybara/rspec/features.rb +17 -48
  37. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  38. data/lib/capybara/rspec/matchers.rb +70 -61
  39. data/lib/capybara/rspec.rb +5 -10
  40. data/lib/capybara/selector/css.rb +6 -11
  41. data/lib/capybara/selector/filter.rb +1 -17
  42. data/lib/capybara/selector/filter_set.rb +18 -15
  43. data/lib/capybara/selector/filters/base.rb +7 -6
  44. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  45. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  46. data/lib/capybara/selector/selector.rb +28 -34
  47. data/lib/capybara/selector.rb +129 -117
  48. data/lib/capybara/selenium/driver.rb +131 -125
  49. data/lib/capybara/selenium/node.rb +197 -115
  50. data/lib/capybara/server.rb +3 -2
  51. data/lib/capybara/session/config.rb +47 -67
  52. data/lib/capybara/session/matchers.rb +8 -7
  53. data/lib/capybara/session.rb +138 -224
  54. data/lib/capybara/spec/public/test.js +25 -4
  55. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  56. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  57. data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
  58. data/lib/capybara/spec/session/all_spec.rb +31 -18
  59. data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
  60. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  61. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  62. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  63. data/lib/capybara/spec/session/assert_text.rb +23 -23
  64. data/lib/capybara/spec/session/assert_title.rb +13 -3
  65. data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
  66. data/lib/capybara/spec/session/body_spec.rb +1 -0
  67. data/lib/capybara/spec/session/check_spec.rb +7 -6
  68. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  69. data/lib/capybara/spec/session/click_button_spec.rb +24 -32
  70. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  71. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  72. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  73. data/lib/capybara/spec/session/current_url_spec.rb +17 -6
  74. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  75. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  76. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  77. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  78. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  79. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
  80. data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
  81. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  82. data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
  83. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  84. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  85. data/lib/capybara/spec/session/find_field_spec.rb +9 -15
  86. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  87. data/lib/capybara/spec/session/find_spec.rb +37 -31
  88. data/lib/capybara/spec/session/first_spec.rb +60 -33
  89. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  90. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  91. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  92. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  93. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  94. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  95. data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
  96. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  97. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  98. data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
  99. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  100. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  101. data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
  102. data/lib/capybara/spec/session/has_select_spec.rb +30 -29
  103. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  104. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  105. data/lib/capybara/spec/session/has_text_spec.rb +9 -13
  106. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  107. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  108. data/lib/capybara/spec/session/headers.rb +2 -1
  109. data/lib/capybara/spec/session/html_spec.rb +1 -0
  110. data/lib/capybara/spec/session/node_spec.rb +91 -56
  111. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  112. data/lib/capybara/spec/session/refresh_spec.rb +6 -2
  113. data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
  114. data/lib/capybara/spec/session/response_code.rb +1 -0
  115. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  116. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  117. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  118. data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
  119. data/lib/capybara/spec/session/select_spec.rb +20 -20
  120. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  121. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  122. data/lib/capybara/spec/session/text_spec.rb +17 -3
  123. data/lib/capybara/spec/session/title_spec.rb +11 -1
  124. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  125. data/lib/capybara/spec/session/unselect_spec.rb +6 -5
  126. data/lib/capybara/spec/session/visit_spec.rb +9 -3
  127. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  128. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  129. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  130. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  131. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  132. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  133. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  134. data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
  135. data/lib/capybara/spec/session/within_spec.rb +1 -0
  136. data/lib/capybara/spec/spec_helper.rb +34 -18
  137. data/lib/capybara/spec/test_app.rb +17 -9
  138. data/lib/capybara/spec/views/form.erb +7 -0
  139. data/lib/capybara/spec/views/with_html.erb +23 -1
  140. data/lib/capybara/spec/views/within_frames.erb +4 -1
  141. data/lib/capybara/version.rb +2 -1
  142. data/lib/capybara/window.rb +6 -10
  143. data/lib/capybara.rb +28 -25
  144. data/spec/basic_node_spec.rb +1 -0
  145. data/spec/capybara_spec.rb +11 -50
  146. data/spec/dsl_spec.rb +5 -13
  147. data/spec/filter_set_spec.rb +5 -4
  148. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  149. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  150. data/spec/minitest_spec.rb +4 -3
  151. data/spec/minitest_spec_spec.rb +3 -2
  152. data/spec/per_session_config_spec.rb +9 -8
  153. data/spec/rack_test_spec.rb +21 -20
  154. data/spec/result_spec.rb +17 -16
  155. data/spec/rspec/features_spec.rb +17 -14
  156. data/spec/rspec/scenarios_spec.rb +5 -7
  157. data/spec/rspec/shared_spec_matchers.rb +96 -99
  158. data/spec/rspec/views_spec.rb +2 -1
  159. data/spec/rspec_matchers_spec.rb +18 -2
  160. data/spec/rspec_spec.rb +11 -15
  161. data/spec/selector_spec.rb +5 -6
  162. data/spec/selenium_spec_chrome.rb +9 -4
  163. data/spec/selenium_spec_edge.rb +27 -0
  164. data/spec/selenium_spec_ie.rb +31 -0
  165. data/spec/selenium_spec_marionette.rb +28 -12
  166. data/spec/server_spec.rb +33 -33
  167. data/spec/session_spec.rb +2 -1
  168. data/spec/shared_selenium_session.rb +36 -22
  169. data/spec/spec_helper.rb +3 -6
  170. metadata +68 -85
  171. data/lib/capybara/query.rb +0 -7
  172. data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'capybara/selector/filter_set'
3
4
  require 'capybara/selector/css'
4
5
  require 'xpath'
5
6
 
6
- #Patch XPath to allow a nil condition in where
7
+ # Patch XPath to allow a nil condition in where
7
8
  module XPath
8
9
  class Renderer
9
10
  undef :where if method_defined?(:where)
@@ -12,7 +13,7 @@ module XPath
12
13
  if !condition.empty?
13
14
  "#{on}[#{condition}]"
14
15
  else
15
- "#{on}"
16
+ on.to_s
16
17
  end
17
18
  end
18
19
  end
@@ -20,12 +21,11 @@ end
20
21
 
21
22
  module Capybara
22
23
  class Selector
23
-
24
24
  attr_reader :name, :format
25
25
 
26
26
  class << self
27
27
  def all
28
- @selectors ||= {}
28
+ @selectors ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
29
29
  end
30
30
 
31
31
  def add(name, &block)
@@ -43,7 +43,7 @@ module Capybara
43
43
 
44
44
  def initialize(name, &block)
45
45
  @name = name
46
- @filter_set = FilterSet.add(name){}
46
+ @filter_set = FilterSet.add(name) {}
47
47
  @match = nil
48
48
  @label = nil
49
49
  @failure_message = nil
@@ -134,7 +134,7 @@ module Capybara
134
134
  # @overload label()
135
135
  # @return [String] The currently set label
136
136
  #
137
- def label(label=nil)
137
+ def label(label = nil)
138
138
  @label = label if label
139
139
  @label
140
140
  end
@@ -146,13 +146,12 @@ module Capybara
146
146
  # @param [Hash] options The options of the query used to generate the description
147
147
  # @return [String] Description of the selector when used with the options passed
148
148
  #
149
- def description(options={})
149
+ def description(**options)
150
150
  @filter_set.description(options)
151
151
  end
152
152
 
153
- def call(locator, options={})
153
+ def call(locator, **options)
154
154
  if format
155
- # @expression.call(locator, options.select {|k,v| @expression_filters.include?(k)})
156
155
  @expression.call(locator, options)
157
156
  else
158
157
  warn "Selector has no format"
@@ -185,15 +184,11 @@ module Capybara
185
184
  # @option options :skip_if Value of the filter that will cause it to be skipped
186
185
  #
187
186
  def filter(name, *types_and_options, &block)
188
- options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
189
- types_and_options.each { |k| options[k] = true }
190
- custom_filters[name] = Filters::NodeFilter.new(name, block, options)
187
+ add_filter(name, Filters::NodeFilter, *types_and_options, &block)
191
188
  end
192
189
 
193
190
  def expression_filter(name, *types_and_options, &block)
194
- options = types_and_options.last.is_a?(Hash) ? types_and_options.pop.dup : {}
195
- types_and_options.each { |k| options[k] = true }
196
- custom_filters[name] = Filters::ExpressionFilter.new(name, block, options)
191
+ add_filter(name, Filters::ExpressionFilter, *types_and_options, &block)
197
192
  end
198
193
 
199
194
  def filter_set(name, filters_to_use = nil)
@@ -205,7 +200,7 @@ module Capybara
205
200
  f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
206
201
  end
207
202
 
208
- def describe &block
203
+ def describe(&block)
209
204
  @filter_set.describe(&block)
210
205
  end
211
206
 
@@ -230,17 +225,22 @@ module Capybara
230
225
  end
231
226
  end
232
227
 
233
- private
228
+ private
229
+
230
+ def add_filter(name, filter_class, *types, **options, &block)
231
+ types.each { |k| options[k] = true }
232
+ custom_filters[name] = filter_class.new(name, block, options)
233
+ end
234
234
 
235
- def locate_field(xpath, locator, options={})
236
- locate_xpath = xpath #need to save original xpath for the label wrap
235
+ def locate_field(xpath, locator, enable_aria_label: false, **_options)
236
+ locate_xpath = xpath # Need to save original xpath for the label wrap
237
237
  if locator
238
238
  locator = locator.to_s
239
- attr_matchers = XPath.attr(:id).equals(locator).or(
240
- XPath.attr(:name).equals(locator)).or(
241
- XPath.attr(:placeholder).equals(locator)).or(
242
- XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)))
243
- attr_matchers = attr_matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
239
+ attr_matchers = [XPath.attr(:id) == locator,
240
+ XPath.attr(:name) == locator,
241
+ XPath.attr(:placeholder) == locator,
242
+ XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
243
+ attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
244
244
 
245
245
  locate_xpath = locate_xpath[attr_matchers]
246
246
  locate_xpath = locate_xpath.union(XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath))
@@ -250,8 +250,8 @@ module Capybara
250
250
  locate_xpath
251
251
  end
252
252
 
253
- def describe_all_expression_filters(opts={})
254
- expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.has_key?(ef) }.join
253
+ def describe_all_expression_filters(**opts)
254
+ expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.key?(ef) }.join
255
255
  end
256
256
 
257
257
  def find_by_attr(attribute, value)
@@ -259,18 +259,12 @@ module Capybara
259
259
  if respond_to?(finder_name, true)
260
260
  send(finder_name, value)
261
261
  else
262
- value ? XPath.attr(attribute).equals(value) : nil
262
+ value ? XPath.attr(attribute) == value : nil
263
263
  end
264
264
  end
265
265
 
266
266
  def find_by_class_attr(classes)
267
- if classes
268
- Array(classes).map do |klass|
269
- "contains(concat(' ',normalize-space(@class),' '),' #{klass} ')"
270
- end.join(" and ").to_sym
271
- else
272
- nil
273
- end
267
+ Array(classes).map { |klass| XPath.attr(:class).contains_word(klass) }.reduce(:&)
274
268
  end
275
269
  end
276
270
  end
@@ -1,27 +1,31 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'capybara/selector/selector'
3
4
  Capybara::Selector::FilterSet.add(:_field) do
4
- filter(:checked, :boolean) { |node, value| not(value ^ node.checked?) }
5
+ filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
5
6
  filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
6
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
7
+ filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
7
8
  filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
8
9
 
9
- expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name).equals(val)] }
10
- expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder).equals(val)] }
10
+ expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
11
+ expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
11
12
 
12
- describe do |options|
13
- desc, states = String.new, []
14
- states << 'checked' if options[:checked] || (options[:unchecked] == false)
15
- states << 'not checked' if options[:unchecked] || (options[:checked] == false)
16
- states << 'disabled' if options[:disabled] == true
17
- states << 'not disabled' if options[:disabled] == false
13
+ describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **_options|
14
+ desc, states = "".dup, []
15
+ states << 'checked' if checked || (unchecked == false)
16
+ states << 'not checked' if unchecked || (checked == false)
17
+ states << 'disabled' if disabled == true
18
+ states << 'not disabled' if disabled == false
18
19
  desc << " that is #{states.join(' and ')}" unless states.empty?
19
- desc << " with the multiple attribute" if options[:multiple] == true
20
- desc << " without the multiple attribute" if options[:multiple] == false
20
+ desc << " with the multiple attribute" if multiple == true
21
+ desc << " without the multiple attribute" if multiple == false
21
22
  desc
22
23
  end
23
24
  end
24
25
 
26
+ # rubocop:disable Metrics/BlockLength
27
+ # rubocop:disable Metrics/ParameterLists
28
+
25
29
  ##
26
30
  #
27
31
  # Select elements by XPath expression
@@ -69,31 +73,31 @@ end
69
73
  # @filter [Boolean] :disabled Match disabled field?
70
74
  # @filter [Boolean] :multiple Match fields that accept multiple values
71
75
  Capybara.add_selector(:field) do
72
- xpath do |locator, options|
73
- xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
76
+ xpath do |locator, **options|
77
+ xpath = XPath.descendant(:input, :textarea, :select)[!XPath.attr(:type).one_of('submit', 'image', 'hidden')]
74
78
  locate_field(xpath, locator, options)
75
79
  end
76
80
 
77
81
  expression_filter(:type) do |expr, type|
78
82
  type = type.to_s
79
- if ['textarea', 'select'].include?(type)
80
- expr.axis(:self, type.to_sym)
83
+ if %w[textarea select].include?(type)
84
+ expr.self(type.to_sym)
81
85
  else
82
- expr[XPath.attr(:type).equals(type)]
86
+ expr[XPath.attr(:type) == type]
83
87
  end
84
88
  end
85
89
 
86
90
  filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
87
91
 
88
- filter(:readonly, :boolean) { |node, value| not(value ^ node.readonly?) }
92
+ filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
89
93
  filter(:with) do |node, with|
90
94
  with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
91
95
  end
92
- describe do |options|
93
- desc = String.new
94
- (expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
95
- desc << " of type #{options[:type].inspect}" if options[:type]
96
- desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
96
+ describe do |type: nil, **options|
97
+ desc = "".dup
98
+ (expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.key?(ef) }
99
+ desc << " of type #{type.inspect}" if type
100
+ desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
97
101
  desc
98
102
  end
99
103
  end
@@ -109,10 +113,10 @@ end
109
113
  # @filter [String, Array<String>] :class Matches the class(es) provided
110
114
  #
111
115
  Capybara.add_selector(:fieldset) do
112
- xpath(:legend) do |locator, options|
116
+ xpath(:legend) do |locator, legend: nil, **_options|
113
117
  xpath = XPath.descendant(:fieldset)
114
- xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
115
- xpath = xpath[XPath.child(:legend)[XPath.string.n.is(options[:legend])]] if options[:legend]
118
+ xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
119
+ xpath = xpath[XPath.child(:legend)[XPath.string.n.is(legend)]] if legend
116
120
  xpath
117
121
  end
118
122
  end
@@ -130,24 +134,24 @@ end
130
134
  # @filter [String, Regexp,nil] :href Matches the normalized href of the link, if nil will find <a> elements with no href attribute
131
135
  #
132
136
  Capybara.add_selector(:link) do
133
- xpath(:title, :alt) do |locator, options={}|
137
+ xpath(:title, :alt) do |locator, href: true, enable_aria_label: false, alt: nil, title: nil, **_options|
134
138
  xpath = XPath.descendant(:a)
135
- xpath = if options.fetch(:href, true).nil?
136
- xpath[~XPath.attr(:href)]
139
+ xpath = if href.nil?
140
+ xpath[!XPath.attr(:href)]
137
141
  else
138
142
  xpath[XPath.attr(:href)]
139
143
  end
140
144
  unless locator.nil?
141
145
  locator = locator.to_s
142
- matchers = XPath.attr(:id).equals(locator).or(
143
- XPath.string.n.is(locator)).or(
144
- XPath.attr(:title).is(locator)).or(
145
- XPath.descendant(:img)[XPath.attr(:alt).is(locator)])
146
- matchers = matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
146
+ matchers = [XPath.attr(:id) == locator,
147
+ XPath.string.n.is(locator),
148
+ XPath.attr(:title).is(locator),
149
+ XPath.descendant(:img)[XPath.attr(:alt).is(locator)]].reduce(:|)
150
+ matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
147
151
  xpath = xpath[matchers]
148
152
  end
149
- xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
150
- xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt).equals(options[:alt])]] if options[:alt]
153
+ xpath = xpath[find_by_attr(:title, title)]
154
+ xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
151
155
  xpath
152
156
  end
153
157
 
@@ -158,12 +162,12 @@ Capybara.add_selector(:link) do
158
162
  when Regexp
159
163
  node[:href].match href
160
164
  else
161
- node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)], minimum: 0)
165
+ node.first(:xpath, XPath.self[XPath.attr(:href) == href.to_s], minimum: 0)
162
166
  end
163
167
  end
164
168
 
165
- describe do |options|
166
- desc = String.new()
169
+ describe do |**options|
170
+ desc = "".dup
167
171
  desc << " with href #{options[:href].inspect}" if options[:href]
168
172
  desc << " with no href attribute" if options.fetch(:href, true).nil?
169
173
  end
@@ -181,22 +185,22 @@ end
181
185
  # @filter [String] :value Matches the value of an input button
182
186
  #
183
187
  Capybara.add_selector(:button) do
184
- xpath(:value, :title) do |locator, options={}|
188
+ xpath(:value, :title) do |locator, **options|
185
189
  input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
186
190
  btn_xpath = XPath.descendant(:button)
187
- image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).equals('image')]
191
+ image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
188
192
 
189
193
  unless locator.nil?
190
194
  locator = locator.to_s
191
- locator_matches = XPath.attr(:id).equals(locator).or XPath.attr(:value).is(locator).or XPath.attr(:title).is(locator)
192
- locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
195
+ locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
196
+ locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
193
197
 
194
198
  input_btn_xpath = input_btn_xpath[locator_matches]
195
199
 
196
- btn_xpath = btn_xpath[locator_matches.or XPath.string.n.is(locator).or XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
200
+ btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
197
201
 
198
202
  alt_matches = XPath.attr(:alt).is(locator)
199
- alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
203
+ alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
200
204
  image_btn_xpath = image_btn_xpath[alt_matches]
201
205
  end
202
206
 
@@ -207,11 +211,11 @@ Capybara.add_selector(:button) do
207
211
  res_xpath
208
212
  end
209
213
 
210
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
214
+ filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
211
215
 
212
- describe do |options|
213
- desc = String.new
214
- desc << " that is disabled" if options[:disabled] == true
216
+ describe do |disabled: nil, **options|
217
+ desc = "".dup
218
+ desc << " that is disabled" if disabled == true
215
219
  desc << describe_all_expression_filters(options)
216
220
  desc
217
221
  end
@@ -223,13 +227,13 @@ end
223
227
  #
224
228
  Capybara.add_selector(:link_or_button) do
225
229
  label "link or button"
226
- xpath do |locator, options|
227
- self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(:union)
230
+ xpath do |locator, **options|
231
+ self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
228
232
  end
229
233
 
230
- filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
234
+ filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or !(value ^ node.disabled?) }
231
235
 
232
- describe { |options| " that is disabled" if options[:disabled] }
236
+ describe { |disabled: nil, **_options| " that is disabled" if disabled == true }
233
237
  end
234
238
 
235
239
  ##
@@ -248,30 +252,31 @@ end
248
252
  #
249
253
  Capybara.add_selector(:fillable_field) do
250
254
  label "field"
251
- xpath do |locator, options|
252
- xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
255
+
256
+ xpath do |locator, **options|
257
+ xpath = XPath.descendant(:input, :textarea)[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
253
258
  locate_field(xpath, locator, options)
254
259
  end
255
260
 
256
261
  expression_filter(:type) do |expr, type|
257
262
  type = type.to_s
258
263
  if ['textarea'].include?(type)
259
- expr.axis(:self, type.to_sym)
264
+ expr.self(type.to_sym)
260
265
  else
261
- expr[XPath.attr(:type).equals(type)]
266
+ expr[XPath.attr(:type) == type]
262
267
  end
263
268
  end
264
269
 
265
- filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
270
+ filter_set(:_field, %i[disabled multiple name placeholder])
266
271
 
267
272
  filter(:with) do |node, with|
268
273
  with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
269
274
  end
270
275
 
271
276
  describe do |options|
272
- desc = String.new
277
+ desc = "".dup
273
278
  desc << describe_all_expression_filters(options)
274
- desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
279
+ desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
275
280
  desc
276
281
  end
277
282
  end
@@ -291,18 +296,19 @@ end
291
296
  #
292
297
  Capybara.add_selector(:radio_button) do
293
298
  label "radio button"
294
- xpath do |locator, options|
295
- xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
299
+
300
+ xpath do |locator, **options|
301
+ xpath = XPath.descendant(:input)[XPath.attr(:type) == 'radio']
296
302
  locate_field(xpath, locator, options)
297
303
  end
298
304
 
299
- filter_set(:_field, [:checked, :unchecked, :disabled, :name])
305
+ filter_set(:_field, %i[checked unchecked disabled name])
300
306
 
301
- filter(:option) { |node, value| node.value == value.to_s }
307
+ filter(:option) { |node, value| node.value == value.to_s }
302
308
 
303
- describe do |options|
304
- desc = String.new
305
- desc << " with value #{options[:option].inspect}" if options[:option]
309
+ describe do |option: nil, **options|
310
+ desc = "".dup
311
+ desc << " with value #{option.inspect}" if option
306
312
  desc << describe_all_expression_filters(options)
307
313
  desc
308
314
  end
@@ -322,18 +328,18 @@ end
322
328
  # @filter [String] :option Match the value
323
329
  #
324
330
  Capybara.add_selector(:checkbox) do
325
- xpath do |locator, options|
326
- xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
331
+ xpath do |locator, **options|
332
+ xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
327
333
  locate_field(xpath, locator, options)
328
334
  end
329
335
 
330
- filter_set(:_field, [:checked, :unchecked, :disabled, :name])
336
+ filter_set(:_field, %i[checked unchecked disabled name])
331
337
 
332
- filter(:option) { |node, value| node.value == value.to_s }
338
+ filter(:option) { |node, value| node.value == value.to_s }
333
339
 
334
- describe do |options|
335
- desc = String.new
336
- desc << " with value #{options[:option].inspect}" if options[:option]
340
+ describe do |option: nil, **options|
341
+ desc = "".dup
342
+ desc << " with value #{option.inspect}" if option
337
343
  desc << describe_all_expression_filters(options)
338
344
  desc
339
345
  end
@@ -357,47 +363,46 @@ end
357
363
  #
358
364
  Capybara.add_selector(:select) do
359
365
  label "select box"
360
- xpath do |locator, options|
366
+
367
+ xpath do |locator, **options|
361
368
  xpath = XPath.descendant(:select)
362
369
  locate_field(xpath, locator, options)
363
370
  end
364
371
 
365
- filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
372
+ filter_set(:_field, %i[disabled multiple name placeholder])
366
373
 
367
374
  filter(:options) do |node, options|
368
- if node.visible?
369
- actual = node.all(:xpath, './/option').map { |option| option.text }
375
+ actual = if node.visible?
376
+ node.all(:xpath, './/option', wait: false).map(&:text)
370
377
  else
371
- actual = node.all(:xpath, './/option', visible: false).map { |option| option.text(:all) }
378
+ node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
372
379
  end
373
380
  options.sort == actual.sort
374
381
  end
375
382
 
376
383
  filter(:with_options) do |node, options|
377
384
  finder_settings = { minimum: 0 }
378
- if !node.visible?
379
- finder_settings[:visible] = false
380
- end
385
+ finder_settings[:visible] = false unless node.visible?
381
386
  options.all? { |option| node.first(:option, option, finder_settings) }
382
387
  end
383
388
 
384
389
  filter(:selected) do |node, selected|
385
- actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
390
+ actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
386
391
  Array(selected).sort == actual.sort
387
392
  end
388
393
 
389
394
  filter(:with_selected) do |node, selected|
390
- actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
395
+ actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
391
396
  (Array(selected) - actual).empty?
392
397
  end
393
398
 
394
- describe do |options|
395
- desc = String.new
396
- desc << " with options #{options[:options].inspect}" if options[:options]
397
- desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
398
- desc << " with #{options[:selected].inspect} selected" if options[:selected]
399
- desc << " with at least #{options[:with_selected].inspect} selected" if options[:with_selected]
400
- desc << describe_all_expression_filters(options)
399
+ describe do |options: nil, with_options: nil, selected: nil, with_selected: nil, **opts|
400
+ desc = "".dup
401
+ desc << " with options #{options.inspect}" if options
402
+ desc << " with at least options #{with_options.inspect}" if with_options
403
+ desc << " with #{selected.inspect} selected" if selected
404
+ desc << " with at least #{with_selected.inspect} selected" if with_selected
405
+ desc << describe_all_expression_filters(opts)
401
406
  desc
402
407
  end
403
408
  end
@@ -417,13 +422,13 @@ Capybara.add_selector(:option) do
417
422
  xpath
418
423
  end
419
424
 
420
- filter(:disabled, :boolean) { |node, value| not(value ^ node.disabled?) }
421
- filter(:selected, :boolean) { |node, value| not(value ^ node.selected?) }
425
+ filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
426
+ filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
422
427
 
423
- describe do |options|
424
- desc = String.new
425
- desc << " that is#{' not' unless options[:disabled]} disabled" if options.has_key?(:disabled)
426
- desc << " that is#{' not' unless options[:selected]} selected" if options.has_key?(:selected)
428
+ describe do |**options|
429
+ desc = "".dup
430
+ desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
431
+ desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
427
432
  desc
428
433
  end
429
434
  end
@@ -442,14 +447,14 @@ end
442
447
  Capybara.add_selector(:file_field) do
443
448
  label "file field"
444
449
  xpath do |locator, options|
445
- xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
450
+ xpath = XPath.descendant(:input)[XPath.attr(:type) == 'file']
446
451
  locate_field(xpath, locator, options)
447
452
  end
448
453
 
449
- filter_set(:_field, [:disabled, :multiple, :name])
454
+ filter_set(:_field, %i[disabled multiple name])
450
455
 
451
- describe do |options|
452
- desc = String.new
456
+ describe do |**options|
457
+ desc = "".dup
453
458
  desc << describe_all_expression_filters(options)
454
459
  desc
455
460
  end
@@ -466,9 +471,13 @@ Capybara.add_selector(:label) do
466
471
  label "label"
467
472
  xpath(:for) do |locator, options|
468
473
  xpath = XPath.descendant(:label)
469
- xpath = xpath[XPath.string.n.is(locator.to_s).or XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
470
- if options.has_key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
471
- xpath = xpath[XPath.attr(:for).equals(options[:for].to_s).or((~XPath.attr(:for)).and(XPath.descendant()[XPath.attr(:id).equals(options[:for].to_s)]))]
474
+ xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
475
+ if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
476
+ with_attr = XPath.attr(:for) == options[:for].to_s
477
+ labelable_elements = %i[button input keygen meter output progress select textarea]
478
+ wrapped = !XPath.attr(:for) &
479
+ XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
480
+ xpath = xpath[with_attr | wrapped]
472
481
  end
473
482
  xpath
474
483
  end
@@ -481,13 +490,13 @@ Capybara.add_selector(:label) do
481
490
  field_or_value.find_xpath('./ancestor::label[1]').include? node.base
482
491
  end
483
492
  else
484
- #Non element values were handled through the expression filter
493
+ # Non element values were handled through the expression filter
485
494
  true
486
495
  end
487
496
  end
488
497
 
489
- describe do |options|
490
- desc = String.new
498
+ describe do |**options|
499
+ desc = "".dup
491
500
  desc << " for #{options[:for]}" if options[:for]
492
501
  desc
493
502
  end
@@ -505,14 +514,14 @@ end
505
514
  Capybara.add_selector(:table) do
506
515
  xpath(:caption) do |locator, options|
507
516
  xpath = XPath.descendant(:table)
508
- xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
509
- xpath = xpath[XPath.descendant(:caption).equals(options[:caption])] if options[:caption]
517
+ xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
518
+ xpath = xpath[XPath.descendant(:caption) == options[:caption]] if options[:caption]
510
519
  xpath
511
520
  end
512
521
 
513
- describe do |options|
514
- desc = String.new
515
- desc << " with caption #{options[:caption]}" if options[:caption]
522
+ describe do |caption: nil, **_options|
523
+ desc = "".dup
524
+ desc << " with caption #{caption}" if caption
516
525
  desc
517
526
  end
518
527
  end
@@ -527,16 +536,19 @@ end
527
536
  # @filter [String, Array<String>] :class Matches the class(es) provided
528
537
  #
529
538
  Capybara.add_selector(:frame) do
530
- xpath(:name) do |locator, options|
539
+ xpath(:name) do |locator, **options|
531
540
  xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
532
- xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.attr(:name).equals(locator)] unless locator.nil?
541
+ xpath = xpath[(XPath.attr(:id) == locator.to_s) | (XPath.attr(:name) == locator.to_s)] unless locator.nil?
533
542
  xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
534
543
  xpath
535
544
  end
536
545
 
537
- describe do |options|
538
- desc = String.new
539
- desc << " with name #{options[:name]}" if options[:name]
546
+ describe do |name: nil, **_options|
547
+ desc = "".dup
548
+ desc << " with name #{name}" if name
540
549
  desc
541
550
  end
542
551
  end
552
+
553
+ # rubocop:enable Metrics/BlockLength
554
+ # rubocop:enable Metrics/ParameterLists