capybara 2.18.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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