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,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)