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,14 +1,30 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::Selenium::Node < Capybara::Driver::Node
4
+ SET_FORMATS = Hash.new(date: '%Y-%m-%d', time: '%H:%M', datetime: "%m%d%Y\t%I%M%P").merge(
5
+ firefox: {
6
+ date: '%Y-%m-%d',
7
+ time: '%H:%M',
8
+ datetime: "%m%d%Y\t%I%M%P"
9
+ },
10
+ chrome: {
11
+ date: '%m%d%Y',
12
+ time: '%I%M%P',
13
+ datetime: "%m%d%Y\t%I%M%P"
14
+ }
15
+ )
3
16
 
4
17
  def visible_text
5
- # Selenium doesn't normalize Unicode whitespace.
6
- Capybara::Helpers.normalize_whitespace(native.text)
18
+ native.text
7
19
  end
8
20
 
9
21
  def all_text
10
22
  text = driver.execute_script("return arguments[0].textContent", self)
11
- Capybara::Helpers.normalize_whitespace(text)
23
+ text.gsub(/[\u200b\u200e\u200f]/, '')
24
+ .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
25
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
26
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
27
+ .tr("\u00a0", ' ')
12
28
  end
13
29
 
14
30
  def [](name)
@@ -36,57 +52,31 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
36
52
  # :none => append the new value to the existing value <br/>
37
53
  # :backspace => send backspace keystrokes to clear the field <br/>
38
54
  # Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
39
- def set(value, options={})
40
- tag_name = self.tag_name
41
- type = self[:type]
42
-
43
- if (Array === value) && !multiple?
44
- raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
45
- end
55
+ def set(value, **options)
56
+ raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
46
57
 
47
58
  case tag_name
48
59
  when 'input'
49
- case type
60
+ case self[:type]
50
61
  when 'radio'
51
62
  click
52
63
  when 'checkbox'
53
- click if value ^ native.attribute('checked').to_s.eql?("true")
64
+ click if value ^ checked?
54
65
  when 'file'
55
- path_names = value.to_s.empty? ? [] : value
56
- if driver.chrome?
57
- native.send_keys(Array(path_names).join("\n"))
58
- else
59
- native.send_keys(*path_names)
60
- end
66
+ set_file(value)
67
+ when 'date'
68
+ set_date(value)
69
+ when 'time'
70
+ set_time(value)
71
+ when 'datetime-local'
72
+ set_datetime_local(value)
61
73
  else
62
74
  set_text(value, options)
63
75
  end
64
76
  when 'textarea'
65
77
  set_text(value, options)
66
78
  else
67
- if content_editable?
68
- #ensure we are focused on the element
69
- click
70
-
71
- script = <<-JS
72
- var range = document.createRange();
73
- var sel = window.getSelection();
74
- arguments[0].focus();
75
- range.selectNodeContents(arguments[0]);
76
- sel.removeAllRanges();
77
- sel.addRange(range);
78
- JS
79
- driver.execute_script script, self
80
-
81
- if driver.chrome? || driver.firefox?
82
- # chromedriver raises a can't focus element for child elements if we use native.send_keys
83
- # we've already focused it so just use action api
84
- driver.browser.action.send_keys(value.to_s).perform
85
- else
86
- # action api is really slow here just use native.send_keys
87
- native.send_keys(value.to_s)
88
- end
89
- end
79
+ set_content_editable(value) if content_editable?
90
80
  end
91
81
  end
92
82
 
@@ -95,32 +85,56 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
95
85
  end
96
86
 
97
87
  def unselect_option
98
- raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." if !select_node.multiple?
88
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
99
89
  native.click if selected?
100
90
  end
101
91
 
102
- def click
103
- native.click
92
+ def click(keys = [], options = {})
93
+ if keys.empty? && !(options[:x] && options[:y])
94
+ native.click
95
+ else
96
+ scroll_if_needed do
97
+ action_with_modifiers(keys, options) do |a|
98
+ if options[:x] && options[:y]
99
+ a.click
100
+ else
101
+ a.click(native)
102
+ end
103
+ end
104
+ end
105
+ end
104
106
  rescue => e
105
107
  if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
106
108
  e.message =~ /Other element would receive the click/
107
109
  begin
108
110
  driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
109
- rescue
111
+ rescue # Swallow error if scrollIntoView with options isn't supported
110
112
  end
111
113
  end
112
114
  raise e
113
115
  end
114
116
 
115
- def right_click
117
+ def right_click(keys = [], options = {})
116
118
  scroll_if_needed do
117
- driver.browser.action.context_click(native).perform
119
+ action_with_modifiers(keys, options) do |a|
120
+ if options[:x] && options[:y]
121
+ a.context_click
122
+ else
123
+ a.context_click(native)
124
+ end
125
+ end
118
126
  end
119
127
  end
120
128
 
121
- def double_click
129
+ def double_click(keys = [], options = {})
122
130
  scroll_if_needed do
123
- driver.browser.action.double_click(native).perform
131
+ action_with_modifiers(keys, options) do |a|
132
+ if options[:x] && options[:y]
133
+ a.double_click
134
+ else
135
+ a.double_click(native)
136
+ end
137
+ end
124
138
  end
125
139
  end
126
140
 
@@ -129,55 +143,38 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
129
143
  end
130
144
 
131
145
  def hover
132
- scroll_if_needed do
133
- driver.browser.action.move_to(native).perform
134
- end
146
+ scroll_if_needed { driver.browser.action.move_to(native).perform }
135
147
  end
136
148
 
137
149
  def drag_to(element)
138
- scroll_if_needed do
139
- driver.browser.action.drag_and_drop(native, element.native).perform
140
- end
150
+ scroll_if_needed { driver.browser.action.drag_and_drop(native, element.native).perform }
141
151
  end
142
152
 
143
153
  def tag_name
144
154
  native.tag_name.downcase
145
155
  end
146
156
 
147
- def visible?
148
- displayed = native.displayed?
149
- displayed and displayed != "false"
150
- end
151
-
152
- def selected?
153
- selected = native.selected?
154
- selected and selected != "false"
155
- end
157
+ def visible?; boolean_attr(native.displayed?); end
158
+ def readonly?; boolean_attr(self[:readonly]); end
159
+ def multiple?; boolean_attr(self[:multiple]); end
160
+ def selected?; boolean_attr(native.selected?); end
156
161
  alias :checked? :selected?
157
162
 
158
163
  def disabled?
164
+ return true unless native.enabled?
165
+
159
166
  # workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
160
167
  if driver.marionette?
161
- if %w(option optgroup).include? tag_name
162
- !native.enabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
168
+ if %w[option optgroup].include? tag_name
169
+ find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
163
170
  else
164
- !native.enabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
171
+ !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
165
172
  end
166
173
  else
167
- !native.enabled?
174
+ false
168
175
  end
169
176
  end
170
177
 
171
- def readonly?
172
- readonly = self[:readonly]
173
- readonly and readonly != "false"
174
- end
175
-
176
- def multiple?
177
- multiple = self[:multiple]
178
- multiple and multiple != "false"
179
- end
180
-
181
178
  def content_editable?
182
179
  native.attribute('isContentEditable')
183
180
  end
@@ -195,63 +192,57 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
195
192
  end
196
193
 
197
194
  def path
198
- path = find_xpath('ancestor::*').reverse
199
- path.unshift self
195
+ path = find_xpath(XPath.ancestor_or_self).reverse
200
196
 
201
197
  result = []
202
- while node = path.shift
198
+ while (node = path.shift)
203
199
  parent = path.first
204
-
200
+ selector = node.tag_name
205
201
  if parent
206
202
  siblings = parent.find_xpath(node.tag_name)
207
- if siblings.size == 1
208
- result.unshift node.tag_name
209
- else
210
- index = siblings.index(node)
211
- result.unshift "#{node.tag_name}[#{index+1}]"
212
- end
213
- else
214
- result.unshift node.tag_name
203
+ selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
215
204
  end
205
+ result.push selector
216
206
  end
217
207
 
218
- '/' + result.join('/')
208
+ '/' + result.reverse.join('/')
219
209
  end
220
210
 
221
211
  private
212
+
213
+ def boolean_attr(val)
214
+ val and val != "false"
215
+ end
216
+
222
217
  # a reference to the select node if this is an option node
223
218
  def select_node
224
- find_xpath('./ancestor::select[1]').first
219
+ find_xpath(XPath.ancestor(:select)[1]).first
225
220
  end
226
221
 
227
- def set_text(value, options)
228
- if readonly?
229
- warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
230
- elsif value.to_s.empty? && options[:clear].nil?
222
+ def set_text(value, clear: nil, **_unused)
223
+ if value.to_s.empty? && clear.nil?
231
224
  native.clear
225
+ elsif clear == :backspace
226
+ # Clear field by sending the correct number of backspace keys.
227
+ backspaces = [:backspace] * self.value.to_s.length
228
+ native.send_keys(*(backspaces + [value.to_s]))
229
+ elsif clear == :none
230
+ native.send_keys(value.to_s)
231
+ elsif clear.is_a? Array
232
+ native.send_keys(*clear, value.to_s)
232
233
  else
233
- if options[:clear] == :backspace
234
- # Clear field by sending the correct number of backspace keys.
235
- backspaces = [:backspace] * self.value.to_s.length
236
- native.send_keys(*(backspaces + [value.to_s]))
237
- elsif options[:clear] == :none
238
- native.send_keys(value.to_s)
239
- elsif options[:clear].is_a? Array
240
- native.send_keys(*options[:clear], value.to_s)
241
- else
242
- # Clear field by JavaScript assignment of the value property.
243
- # Script can change a readonly element which user input cannot, so
244
- # don't execute if readonly.
245
- driver.execute_script "arguments[0].value = ''", self
246
- native.send_keys(value.to_s)
247
- end
234
+ # Clear field by JavaScript assignment of the value property.
235
+ # Script can change a readonly element which user input cannot, so
236
+ # don't execute if readonly.
237
+ driver.execute_script "arguments[0].value = ''", self
238
+ native.send_keys(value.to_s)
248
239
  end
249
240
  end
250
241
 
251
- def scroll_if_needed(&block)
252
- block.call
242
+ def scroll_if_needed
243
+ yield
253
244
  rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
254
- script = <<-JS
245
+ script = <<-'JS'
255
246
  try {
256
247
  arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
257
248
  } catch(e) {
@@ -259,6 +250,97 @@ private
259
250
  }
260
251
  JS
261
252
  driver.execute_script(script, self)
262
- block.call
253
+ yield
254
+ end
255
+
256
+ def set_date(value) # rubocop:disable Naming/AccessorMethodName
257
+ if value.respond_to?(:to_date)
258
+ set_text(value.to_date.strftime(SET_FORMATS[driver.options[:browser].to_sym][:date]))
259
+ else
260
+ set_text(value)
261
+ end
262
+ end
263
+
264
+ def set_time(value) # rubocop:disable Naming/AccessorMethodName
265
+ if value.respond_to?(:to_time)
266
+ set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:time]))
267
+ else
268
+ set_text(value)
269
+ end
270
+ end
271
+
272
+ def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
273
+ if value.respond_to?(:to_time)
274
+ set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:datetime]))
275
+ else
276
+ set_text(value)
277
+ end
278
+ end
279
+
280
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
281
+ path_names = value.to_s.empty? ? [] : value
282
+ if driver.marionette?
283
+ native.clear
284
+ Array(path_names).each { |p| native.send_keys(p) }
285
+ else
286
+ native.send_keys(Array(path_names).join("\n"))
287
+ end
288
+ end
289
+
290
+ def set_content_editable(value) # rubocop:disable Naming/AccessorMethodName
291
+ # Ensure we are focused on the element
292
+ click
293
+
294
+ script = <<-JS
295
+ var range = document.createRange();
296
+ var sel = window.getSelection();
297
+ arguments[0].focus();
298
+ range.selectNodeContents(arguments[0]);
299
+ sel.removeAllRanges();
300
+ sel.addRange(range);
301
+ JS
302
+ driver.execute_script script, self
303
+
304
+ # The action api has a speed problem but both chrome and firefox 58 raise errors
305
+ # if we use the faster direct send_keys. For now just send_keys to the element
306
+ # we've already focused.
307
+ # native.send_keys(value.to_s)
308
+ driver.browser.action.send_keys(value.to_s).perform
309
+ end
310
+
311
+ def action_with_modifiers(keys, x: nil, y: nil)
312
+ actions = driver.browser.action
313
+ actions.move_to(native, x, y)
314
+ modifiers_down(actions, keys)
315
+ yield actions
316
+ modifiers_up(actions, keys)
317
+ actions.perform
318
+ ensure
319
+ a = driver.browser.action
320
+ a.release_actions if a.respond_to?(:release_actions)
321
+ end
322
+
323
+ def modifiers_down(actions, keys)
324
+ keys.each do |key|
325
+ key = case key
326
+ when :ctrl then :control
327
+ when :command, :cmd then :meta
328
+ else
329
+ key
330
+ end
331
+ actions.key_down(key)
332
+ end
333
+ end
334
+
335
+ def modifiers_up(actions, keys)
336
+ keys.each do |key|
337
+ key = case key
338
+ when :ctrl then :control
339
+ when :command, :cmd then :meta
340
+ else
341
+ key
342
+ end
343
+ actions.key_up(key)
344
+ end
263
345
  end
264
346
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'uri'
3
4
  require 'net/http'
4
5
  require 'rack'
@@ -43,7 +44,7 @@ module Capybara
43
44
  begin
44
45
  @app.call(env)
45
46
  rescue *@server_errors => e
46
- @error = e unless @error
47
+ @error ||= e
47
48
  raise e
48
49
  ensure
49
50
  @counter.decrement
@@ -60,7 +61,7 @@ module Capybara
60
61
 
61
62
  attr_reader :app, :port, :host
62
63
 
63
- def initialize(app, port=Capybara.server_port, host=Capybara.server_host, server_errors=Capybara.server_errors)
64
+ def initialize(app, port = Capybara.server_port, host = Capybara.server_host, server_errors = Capybara.server_errors)
64
65
  @app = app
65
66
  @server_thread = nil # suppress warnings
66
67
  @host, @port, @server_errors = host, port, server_errors
@@ -1,61 +1,55 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'delegate'
3
4
 
4
5
  module Capybara
5
6
  class SessionConfig
6
- OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
7
- :automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
8
- :automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
9
- :save_and_open_page_path, :server_host, :server_port, :server_errors]
7
+ OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
8
+ automatic_reload match exact exact_text raise_server_errors visible_text_only
9
+ automatic_label_click enable_aria_label save_path asset_host default_host app_host
10
+ server_host server_port server_errors].freeze
10
11
 
11
12
  attr_accessor(*OPTIONS)
12
13
 
13
14
  ##
14
- #@!method always_include_port
15
- # See {Capybara.configure}
16
- #@!method run_server
17
- # See {Capybara.configure}
18
- #@!method default_selector
19
- # See {Capybara.configure}
20
- #@!method default_max_wait_time
21
- # See {Capybara.configure}
22
- #@!method ignore_hidden_elements
23
- # See {Capybara.configure}
24
- #@!method automatic_reload
25
- # See {Capybara.configure}
26
- #@!method match
27
- # See {Capybara.configure}
28
- #@!method exact
29
- # See {Capybara.configure}
30
- #@!method raise_server_errors
31
- # See {Capybara.configure}
32
- #@!method visible_text_only
33
- # See {Capybara.configure}
34
- #@!method wait_on_first_by_default
35
- # See {Capybara.configure}
36
- #@!method automatic_label_click
37
- # See {Capybara.configure}
38
- #@!method enable_aria_label
39
- # See {Capybara.configure}
40
- #@!method save_path
41
- # See {Capybara.configure}
42
- #@deprecated
43
- #@!method exact_options
44
- # See {Capybara.configure}
45
- #@!method asset_host
46
- # See {Capybara.configure}
47
- #@!method default_host
48
- # See {Capybara.configure}
49
- #@!method app_host
50
- # See {Capybara.configure}
51
- #@!method save_and_open_page_path
52
- # See {Capybara.configure}
53
- #@!method server_host
54
- # See {Capybara.configure}
55
- #@!method server_port
56
- # See {Capybara.configure}
57
- #@!method server_errors
58
- # See {Capybara.configure}
15
+ # @!method always_include_port
16
+ # See {Capybara.configure}
17
+ # @!method run_server
18
+ # See {Capybara.configure}
19
+ # @!method default_selector
20
+ # See {Capybara.configure}
21
+ # @!method default_max_wait_time
22
+ # See {Capybara.configure}
23
+ # @!method ignore_hidden_elements
24
+ # See {Capybara.configure}
25
+ # @!method automatic_reload
26
+ # See {Capybara.configure}
27
+ # @!method match
28
+ # See {Capybara.configure}
29
+ # @!method exact
30
+ # See {Capybara.configure}
31
+ # @!method raise_server_errors
32
+ # See {Capybara.configure}
33
+ # @!method visible_text_only
34
+ # See {Capybara.configure}
35
+ # @!method automatic_label_click
36
+ # See {Capybara.configure}
37
+ # @!method enable_aria_label
38
+ # See {Capybara.configure}
39
+ # @!method save_path
40
+ # See {Capybara.configure}
41
+ # @!method asset_host
42
+ # See {Capybara.configure}
43
+ # @!method default_host
44
+ # See {Capybara.configure}
45
+ # @!method app_host
46
+ # See {Capybara.configure}
47
+ # @!method server_host
48
+ # See {Capybara.configure}
49
+ # @!method server_port
50
+ # See {Capybara.configure}
51
+ # @!method server_errors
52
+ # See {Capybara.configure}
59
53
 
60
54
  remove_method :server_host
61
55
 
@@ -74,30 +68,16 @@ module Capybara
74
68
 
75
69
  remove_method :app_host=
76
70
  def app_host=(url)
77
- raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
71
+ raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
78
72
  @app_host = url
79
73
  end
80
74
 
81
75
  remove_method :default_host=
82
76
  def default_host=(url)
83
- raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
77
+ raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
84
78
  @default_host = url
85
79
  end
86
80
 
87
- remove_method :save_and_open_page_path=
88
- def save_and_open_page_path=(path)
89
- warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
90
- "Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
91
- @save_and_open_page_path = path
92
- end
93
-
94
- remove_method :exact_options=
95
- def exact_options=(opt)
96
- @exact_options = opt
97
- warn "DEPRECATED: #exact_options is deprecated, please scope your findes/actions and use the `:exact` "\
98
- "option if similar functionality is needed."
99
- end
100
-
101
81
  def initialize_copy(other)
102
82
  super
103
83
  @server_errors = @server_errors.dup
@@ -106,9 +86,9 @@ module Capybara
106
86
 
107
87
  class ReadOnlySessionConfig < SimpleDelegator
108
88
  SessionConfig::OPTIONS.each do |m|
109
- define_method "#{m}=" do |val|
89
+ define_method "#{m}=" do |_|
110
90
  raise "Per session settings are only supported when Capybara.threadsafe == true"
111
91
  end
112
92
  end
113
93
  end
114
- end
94
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module SessionMatchers
4
5
  ##
@@ -18,8 +19,8 @@ module Capybara
18
19
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
19
20
  # @return [true]
20
21
  #
21
- def assert_current_path(path, options={})
22
- _verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
22
+ def assert_current_path(path, **options)
23
+ _verify_current_path(path, options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
23
24
  end
24
25
 
25
26
  ##
@@ -32,8 +33,8 @@ module Capybara
32
33
  # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
33
34
  # @return [true]
34
35
  #
35
- def assert_no_current_path(path, options={})
36
- _verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
36
+ def assert_no_current_path(path, **options)
37
+ _verify_current_path(path, options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
37
38
  end
38
39
 
39
40
  ##
@@ -45,7 +46,7 @@ module Capybara
45
46
  # @macro current_path_query_params
46
47
  # @return [Boolean]
47
48
  #
48
- def has_current_path?(path, options={})
49
+ def has_current_path?(path, **options)
49
50
  assert_current_path(path, options)
50
51
  rescue Capybara::ExpectationNotMet
51
52
  return false
@@ -60,13 +61,13 @@ module Capybara
60
61
  # @macro current_path_query_params
61
62
  # @return [Boolean]
62
63
  #
63
- def has_no_current_path?(path, options={})
64
+ def has_no_current_path?(path, **options)
64
65
  assert_no_current_path(path, options)
65
66
  rescue Capybara::ExpectationNotMet
66
67
  return false
67
68
  end
68
69
 
69
- private
70
+ private
70
71
 
71
72
  def _verify_current_path(path, options)
72
73
  query = Capybara::Queries::CurrentPathQuery.new(path, options)