capybara 3.0.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +10 -0
  3. data/lib/capybara.rb +1 -1
  4. data/lib/capybara/helpers.rb +1 -1
  5. data/lib/capybara/minitest/spec.rb +2 -0
  6. data/lib/capybara/node/actions.rb +45 -5
  7. data/lib/capybara/queries/match_query.rb +2 -0
  8. data/lib/capybara/queries/selector_query.rb +3 -4
  9. data/lib/capybara/queries/text_query.rb +4 -5
  10. data/lib/capybara/rack_test/node.rb +6 -4
  11. data/lib/capybara/result.rb +1 -1
  12. data/lib/capybara/rspec/compound.rb +2 -0
  13. data/lib/capybara/selector.rb +72 -18
  14. data/lib/capybara/selector/css.rb +2 -0
  15. data/lib/capybara/selenium/driver.rb +14 -15
  16. data/lib/capybara/selenium/node.rb +23 -29
  17. data/lib/capybara/server.rb +29 -4
  18. data/lib/capybara/session.rb +12 -13
  19. data/lib/capybara/spec/session/all_spec.rb +6 -6
  20. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +2 -3
  21. data/lib/capybara/spec/session/{assert_selector.rb → assert_selector_spec.rb} +0 -0
  22. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +1 -1
  23. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +0 -0
  24. data/lib/capybara/spec/session/check_spec.rb +6 -6
  25. data/lib/capybara/spec/session/click_link_or_button_spec.rb +0 -7
  26. data/lib/capybara/spec/session/current_url_spec.rb +6 -8
  27. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +2 -0
  28. data/lib/capybara/spec/session/element/match_css_spec.rb +2 -0
  29. data/lib/capybara/spec/session/element/match_xpath_spec.rb +2 -0
  30. data/lib/capybara/spec/session/element/matches_selector_spec.rb +2 -0
  31. data/lib/capybara/spec/session/find_by_id_spec.rb +1 -1
  32. data/lib/capybara/spec/session/find_field_spec.rb +1 -1
  33. data/lib/capybara/spec/session/first_spec.rb +12 -12
  34. data/lib/capybara/spec/session/frame/frame_title_spec.rb +1 -1
  35. data/lib/capybara/spec/session/frame/frame_url_spec.rb +1 -1
  36. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -2
  37. data/lib/capybara/spec/session/frame/within_frame_spec.rb +1 -1
  38. data/lib/capybara/spec/session/has_button_spec.rb +5 -0
  39. data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
  40. data/lib/capybara/spec/session/has_field_spec.rb +1 -1
  41. data/lib/capybara/spec/session/has_none_selectors_spec.rb +2 -0
  42. data/lib/capybara/spec/session/has_selector_spec.rb +8 -0
  43. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +0 -0
  44. data/lib/capybara/spec/session/node_spec.rb +4 -4
  45. data/lib/capybara/spec/session/reset_session_spec.rb +2 -2
  46. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +0 -0
  47. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -1
  48. data/lib/capybara/spec/session/select_spec.rb +27 -1
  49. data/lib/capybara/spec/session/selectors_spec.rb +2 -0
  50. data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
  51. data/lib/capybara/spec/session/visit_spec.rb +17 -10
  52. data/lib/capybara/spec/session/window/become_closed_spec.rb +3 -3
  53. data/lib/capybara/spec/session/window/current_window_spec.rb +2 -2
  54. data/lib/capybara/spec/session/window/open_new_window_spec.rb +3 -3
  55. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +5 -5
  56. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +3 -3
  57. data/lib/capybara/spec/session/window/window_spec.rb +24 -9
  58. data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
  59. data/lib/capybara/spec/session/window/within_window_spec.rb +2 -2
  60. data/lib/capybara/spec/session/within_spec.rb +3 -3
  61. data/lib/capybara/spec/spec_helper.rb +7 -5
  62. data/lib/capybara/spec/views/form.erb +9 -0
  63. data/lib/capybara/version.rb +1 -1
  64. data/spec/basic_node_spec.rb +1 -1
  65. data/spec/capybara_spec.rb +4 -14
  66. data/spec/dsl_spec.rb +5 -3
  67. data/spec/fixtures/certificate.pem +25 -0
  68. data/spec/fixtures/key.pem +27 -0
  69. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
  70. data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
  71. data/spec/rack_test_spec.rb +8 -6
  72. data/spec/result_spec.rb +22 -2
  73. data/spec/rspec/features_spec.rb +4 -2
  74. data/spec/rspec/scenarios_spec.rb +1 -1
  75. data/spec/rspec/shared_spec_matchers.rb +7 -4
  76. data/spec/rspec/views_spec.rb +2 -1
  77. data/spec/rspec_spec.rb +80 -78
  78. data/spec/selenium_spec_marionette.rb +6 -4
  79. data/spec/server_spec.rb +40 -2
  80. data/spec/session_spec.rb +12 -4
  81. data/spec/shared_selenium_session.rb +106 -84
  82. metadata +11 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8304f8ac3e0f20f8fae8db3eedea4f623de6756708c0ba95ec83fc4587ec4321
4
- data.tar.gz: dff277e0f6abb0275ff4462de9fd9de8c930032f4a574448af16688af337c58c
3
+ metadata.gz: 75ca733cee9a96dd5d17e4ae8ed453686c73d4deaf77fae6fa4008b13b62e12d
4
+ data.tar.gz: ed22e70d57b1baac7bf6aafd9f483acf16f07193fb087e872b94d24b44134165
5
5
  SHA512:
6
- metadata.gz: 7b7bfa133c75b1556490b7337178cfd3e9137157e16c922679b72067b055fd54d659237367c7f066bbb7e2a6d75311db8736abc8e4d94030898a39e39a297ae9
7
- data.tar.gz: b320cdc763f873e87f13b83ec693db1f590a32a2193fb56a7dfd82310101b39636c26a1da520b723dca31672f289201d649b90a0288f1ae281ac40b6b3d612bc
6
+ metadata.gz: a221f4ae1099fa5367af6c60396263d35f1d1d0fdcc8d5fda712a4ca8f3b32e3ca6de14fae15dcf39e2a76f24c91555a6ac2c5745d4d9185ecbb80a0a34e463b
7
+ data.tar.gz: 41034edf9b5cf28ab1850a9f499996a4c3903cb75d77112c7d83fe342e7b097a89a83bdfff36750294bf113ba65d07e3db4500eeff8e618e48fa5e55c4e405c8
data/History.md CHANGED
@@ -1,3 +1,13 @@
1
+ # Version 3.1.0
2
+ Release date: 2018-05-10
3
+
4
+ ### Added
5
+
6
+ * Support for using `select` with text inputs associated with a datalist element
7
+ * `type` filter on `:button` selector
8
+ * Support for server operating in https mode
9
+ * Selenium driver now uses JS to fill_in/set date and time fields when passed date or time objects [Aleksei Gusev, Thomas Walpole]
10
+
1
11
  # Version 3.0.3
2
12
  Release date: 2018-04-30
3
13
 
@@ -497,6 +497,6 @@ Capybara.register_driver :selenium_chrome_headless do |app|
497
497
  Capybara::Selenium::Driver.load_selenium
498
498
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
499
499
  browser_options.args << '--headless'
500
- browser_options.args << '--disable-gpu'
500
+ browser_options.args << '--disable-gpu' if Gem.win_platform?
501
501
  Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
502
502
  end
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  # @api private
5
5
  module Helpers
6
- extend self
6
+ module_function
7
7
 
8
8
  ##
9
9
  # @deprecated
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'minitest/spec'
2
4
 
3
5
  module Capybara
@@ -158,8 +158,8 @@ module Capybara
158
158
 
159
159
  ##
160
160
  #
161
- # If `:from` option is present, `select` finds a select box on the page
162
- # and selects a particular option from it.
161
+ # If `:from` option is present, `select` finds a select box, or text input with associated datalist,
162
+ # on the page and selects a particular option from it.
163
163
  # Otherwise it finds an option inside current scope and selects it.
164
164
  # If the select box is a multiple select, +select+ can be called multiple times to select more than
165
165
  # one option.
@@ -169,13 +169,47 @@ module Capybara
169
169
  #
170
170
  # @macro waiting_behavior
171
171
  #
172
- # @param [String] value Which option to select
172
+ # @param [String] value Which option to select
173
173
  # @option options [String] :from The id, name or label of the select box
174
174
  #
175
175
  # @return [Capybara::Node::Element] The option element selected
176
176
  def select(value = nil, from: nil, **options)
177
- scope = from ? find(:select, from, options) : self
178
- scope.find(:option, value, options).select_option
177
+ scope = if from
178
+ synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
179
+ begin
180
+ find(:select, from, options)
181
+ rescue Capybara::ElementNotFound => select_error
182
+ raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
183
+ begin
184
+ find(:datalist_input, from, options)
185
+ rescue Capybara::ElementNotFound => dlinput_error
186
+ raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
187
+ end
188
+ end
189
+ end
190
+ else
191
+ self
192
+ end
193
+
194
+ if scope.respond_to?(:tag_name) && scope.tag_name == "input"
195
+ begin
196
+ # TODO: this is a more efficient but won't work with non-JS drivers
197
+ # datalist_options = session.evaluate_script('Array.prototype.slice.call((arguments[0].list||{}).options || []).filter(function(el){ return !el.disabled }).map(function(el){ return { "value": el.value, "label": el.label} })', scope)
198
+ datalist_options = session.evaluate_script(DATALIST_OPTIONS_SCRIPT, scope)
199
+ if (option = datalist_options.find { |o| o['value'] == value || o['label'] == value })
200
+ scope.set(option["value"])
201
+ else
202
+ raise ::Capybara::ElementNotFound, "Unable to find datalist option \"#{value}\""
203
+ end
204
+ rescue ::Capybara::NotSupportedByDriverError
205
+ # Implement for drivers that don't support JS
206
+ datalist = find(:xpath, XPath.descendant(:datalist)[XPath.attr(:id) == scope[:list]], visible: false)
207
+ option = datalist.find(:datalist_option, value, disabled: false)
208
+ scope.set(option.value)
209
+ end
210
+ else
211
+ scope.find(:option, value, options).select_option
212
+ end
179
213
  end
180
214
 
181
215
  ##
@@ -291,6 +325,12 @@ module Capybara
291
325
  delete el.capybara_style_cache;
292
326
  }
293
327
  JS
328
+
329
+ DATALIST_OPTIONS_SCRIPT = <<-'JS'.freeze
330
+ Array.prototype.slice.call((arguments[0].list||{}).options || []).
331
+ filter(function(el){ return !el.disabled }).
332
+ map(function(el){ return { "value": el.value, "label": el.label} })
333
+ JS
294
334
  end
295
335
  end
296
336
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Capybara
2
4
  module Queries
3
5
  class MatchQuery < Capybara::Queries::SelectorQuery
@@ -35,7 +35,7 @@ module Capybara
35
35
  @description << "visible " if visible == :visible
36
36
  @description << "non-visible " if visible == :hidden
37
37
  @description << "#{label} #{locator.inspect}"
38
- @description << " with#{" exact" if exact_text == true} text #{options[:text].inspect}" if options[:text]
38
+ @description << " with#{' exact' if exact_text == true} text #{options[:text].inspect}" if options[:text]
39
39
  @description << " with exact text #{options[:exact_text]}" if options[:exact_text].is_a?(String)
40
40
  @description << " with id #{options[:id]}" if options[:id]
41
41
  @description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
@@ -167,9 +167,8 @@ module Capybara
167
167
 
168
168
  def assert_valid_keys
169
169
  super
170
- unless VALID_MATCH.include?(match)
171
- raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
172
- end
170
+ return if VALID_MATCH.include?(match)
171
+ raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}"
173
172
  end
174
173
 
175
174
  def filtered_xpath(expr)
@@ -39,7 +39,7 @@ module Capybara
39
39
  if @expected_text.is_a?(Regexp)
40
40
  "text matching #{@expected_text.inspect}"
41
41
  else
42
- "#{"exact " if exact?}text #{@expected_text.inspect}"
42
+ "#{'exact ' if exact?}text #{@expected_text.inspect}"
43
43
  end
44
44
  end
45
45
 
@@ -68,16 +68,15 @@ module Capybara
68
68
  def case_insensitive_message
69
69
  insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
70
70
  insensitive_count = @actual_text.scan(insensitive_regexp).size
71
- if insensitive_count != @count
72
- "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
73
- end
71
+ return if insensitive_count == @count
72
+ "it was found #{insensitive_count} #{Capybara::Helpers.declension('time', 'times', insensitive_count)} using a case insensitive search"
74
73
  end
75
74
 
76
75
  def invisible_message
77
76
  invisible_text = text(@node, :all)
78
77
  invisible_count = invisible_text.scan(@search_regexp).size
79
78
  if invisible_count != @count
80
- "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
79
+ "it was found #{invisible_count} #{Capybara::Helpers.declension('time', 'times', invisible_count)} including non-visible text"
81
80
  end
82
81
  rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
83
82
  end
@@ -90,7 +90,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
90
90
  return true if string_node.disabled?
91
91
 
92
92
  if %w[option optgroup].include? tag_name
93
- find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
93
+ find_xpath("parent::*[self::optgroup or self::select or self::datalist]")[0].disabled?
94
94
  else
95
95
  !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
96
96
  end
@@ -210,9 +210,7 @@ private
210
210
  find_xpath(".//input")
211
211
  end.first
212
212
 
213
- if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
214
- labelled_control.set(!labelled_control.checked?)
215
- end
213
+ labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
216
214
  end
217
215
 
218
216
  def link?
@@ -229,6 +227,10 @@ private
229
227
 
230
228
  protected
231
229
 
230
+ def checkbox_or_radio?(field = self)
231
+ field && (field.checkbox? || field.radio?)
232
+ end
233
+
232
234
  def checkbox?
233
235
  input_field? && type == 'checkbox'
234
236
  end
@@ -114,7 +114,7 @@ module Capybara
114
114
  if count.zero?
115
115
  message << " but there were no matches"
116
116
  else
117
- message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
117
+ message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " << full_results.map(&:text).map(&:inspect).join(", ")
118
118
  end
119
119
  unless rest.empty?
120
120
  elements = rest.map { |el| el.text rescue "<<ERROR>>" }.map(&:inspect).join(", ")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Capybara
2
4
  module RSpecMatchers
3
5
  module Compound
@@ -136,11 +136,19 @@ end
136
136
  Capybara.add_selector(:link) do
137
137
  xpath(:title, :alt) do |locator, href: true, enable_aria_label: false, alt: nil, title: nil, **_options|
138
138
  xpath = XPath.descendant(:a)
139
- xpath = if href.nil?
140
- xpath[!XPath.attr(:href)]
141
- else
142
- xpath[XPath.attr(:href)]
143
- end
139
+ xpath = xpath[
140
+ case href
141
+ when nil, false
142
+ !XPath.attr(:href)
143
+ when true
144
+ XPath.attr(:href)
145
+ when Regexp
146
+ nil # needs to be handled in filter
147
+ else
148
+ XPath.attr(:href) == href.to_s
149
+ end
150
+ ]
151
+
144
152
  unless locator.nil?
145
153
  locator = locator.to_s
146
154
  matchers = [XPath.attr(:id) == locator,
@@ -150,20 +158,15 @@ Capybara.add_selector(:link) do
150
158
  matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
151
159
  xpath = xpath[matchers]
152
160
  end
161
+
153
162
  xpath = xpath[find_by_attr(:title, title)]
154
163
  xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
155
164
  xpath
156
165
  end
157
166
 
158
167
  filter(:href) do |node, href|
159
- case href
160
- when nil
161
- true
162
- when Regexp
163
- node[:href].match href
164
- else
165
- node.first(:xpath, XPath.self[XPath.attr(:href) == href.to_s], minimum: 0)
166
- end
168
+ # If not a Regexp it's been handled in the main XPath
169
+ href.is_a?(Regexp) ? node[:href].match(href) : true
167
170
  end
168
171
 
169
172
  describe do |**options|
@@ -185,7 +188,7 @@ end
185
188
  # @filter [String] :value Matches the value of an input button
186
189
  #
187
190
  Capybara.add_selector(:button) do
188
- xpath(:value, :title) do |locator, **options|
191
+ xpath(:value, :title, :type) do |locator, **options|
189
192
  input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
190
193
  btn_xpath = XPath.descendant(:button)
191
194
  image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
@@ -380,10 +383,11 @@ Capybara.add_selector(:select) do
380
383
  options.sort == actual.sort
381
384
  end
382
385
 
383
- filter(:with_options) do |node, options|
384
- finder_settings = { minimum: 0 }
385
- finder_settings[:visible] = false unless node.visible?
386
- options.all? { |option| node.first(:option, option, finder_settings) }
386
+ expression_filter(:with_options) do |expr, options|
387
+ options.each do |option|
388
+ expr = expr[Capybara::Selector.all[:option].call(option)]
389
+ end
390
+ expr
387
391
  end
388
392
 
389
393
  filter(:selected) do |node, selected|
@@ -407,6 +411,37 @@ Capybara.add_selector(:select) do
407
411
  end
408
412
  end
409
413
 
414
+ Capybara.add_selector(:datalist_input) do
415
+ label "input box with datalist completion"
416
+
417
+ xpath do |locator, **options|
418
+ xpath = XPath.descendant(:input)[XPath.attr(:list)]
419
+ locate_field(xpath, locator, options)
420
+ end
421
+
422
+ filter_set(:_field, %i[disabled name placeholder])
423
+
424
+ filter(:options) do |node, options|
425
+ actual = node.find("//datalist[@id=#{node[:list]}]", visible: :all).all(:datalist_option, wait: false).map(&:value)
426
+ options.sort == actual.sort
427
+ end
428
+
429
+ expression_filter(:with_options) do |expr, options|
430
+ options.each do |option|
431
+ expr = expr[XPath.attr(:list) == XPath.anywhere(:datalist)[Capybara::Selector.all[:datalist_option].call(option)].attr(:id)]
432
+ end
433
+ expr
434
+ end
435
+
436
+ describe do |options: nil, with_options: nil, **opts|
437
+ desc = "".dup
438
+ desc << " with options #{options.inspect}" if options
439
+ desc << " with at least options #{with_options.inspect}" if with_options
440
+ desc << describe_all_expression_filters(opts)
441
+ desc
442
+ end
443
+ end
444
+
410
445
  ##
411
446
  #
412
447
  # Find option elements
@@ -433,6 +468,25 @@ Capybara.add_selector(:option) do
433
468
  end
434
469
  end
435
470
 
471
+ Capybara.add_selector(:datalist_option) do
472
+ label "datalist option"
473
+ visible(:all)
474
+
475
+ xpath do |locator|
476
+ xpath = XPath.descendant(:option)
477
+ xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:value) == locator.to_s)] unless locator.nil?
478
+ xpath
479
+ end
480
+
481
+ filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
482
+
483
+ describe do |**options|
484
+ desc = "".dup
485
+ desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
486
+ desc
487
+ end
488
+ end
489
+
436
490
  ##
437
491
  #
438
492
  # Find file input elements
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Capybara
2
4
  class Selector
3
5
  class CSS
@@ -23,11 +23,8 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
23
23
  ::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
24
24
  end
25
25
  rescue LoadError => e
26
- if e.message =~ /selenium-webdriver/
27
- raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
28
- else
29
- raise e
30
- end
26
+ raise e if e.message !~ /selenium-webdriver/
27
+ raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
31
28
  end
32
29
 
33
30
  def browser
@@ -305,22 +302,27 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
305
302
 
306
303
  # @api private
307
304
  def firefox?
308
- browser.browser == :firefox
305
+ browser_name == :firefox
309
306
  end
310
307
 
311
308
  # @api private
312
309
  def chrome?
313
- browser.browser == :chrome
310
+ browser_name == :chrome
314
311
  end
315
312
 
316
313
  # @api private
317
314
  def edge?
318
- browser.browser == :edge
315
+ browser_name == :edge
319
316
  end
320
317
 
321
318
  # @api private
322
319
  def ie?
323
- browser.browser == :ie
320
+ browser_name == :ie
321
+ end
322
+
323
+ # @api private
324
+ def browser_name
325
+ browser.browser
324
326
  end
325
327
 
326
328
  private
@@ -355,7 +357,7 @@ private
355
357
  if response_text.nil?
356
358
  "default_text"
357
359
  else
358
- "'#{response_text.gsub("\\", "\\\\\\").gsub("'", "\\\\'")}'"
360
+ "'#{response_text.gsub('\\', '\\\\\\').gsub("'", "\\\\'")}'"
359
361
  end
360
362
  else
361
363
  'null'
@@ -447,11 +449,8 @@ private
447
449
  if called
448
450
  execute_script('window.capybara && window.capybara.modal_handlers.shift()')
449
451
  regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
450
- if alert_text.match(regexp)
451
- alert_text
452
- else
453
- raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
454
- end
452
+ raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}" unless alert_text.match(regexp)
453
+ alert_text
455
454
  elsif called.nil?
456
455
  # page changed so modal_handler data has gone away
457
456
  warn "Can't verify modal text when page change occurs - ignoring" if options[:text]
@@ -1,19 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
- )
16
-
17
4
  def visible_text
18
5
  native.text
19
6
  end
@@ -199,7 +186,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
199
186
  parent = path.first
200
187
  selector = node.tag_name
201
188
  if parent
202
- siblings = parent.find_xpath(node.tag_name)
189
+ siblings = parent.find_xpath(selector)
203
190
  selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
204
191
  end
205
192
  result.push selector
@@ -254,27 +241,34 @@ private
254
241
  end
255
242
 
256
243
  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.browser.browser][:date]))
259
- else
260
- set_text(value)
261
- end
244
+ return set_text(value) unless value.respond_to?(:to_date)
245
+ # TODO: this would be better if locale can be detected and correct keystrokes sent
246
+ update_value_js(value.to_date.strftime('%Y-%m-%d'))
262
247
  end
263
248
 
264
249
  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.browser.browser][:time]))
267
- else
268
- set_text(value)
269
- end
250
+ return set_text(value) unless value.respond_to?(:to_time)
251
+ # TODO: this would be better if locale can be detected and correct keystrokes sent
252
+ update_value_js(value.to_time.strftime('%H:%M'))
270
253
  end
271
254
 
272
255
  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.browser.browser][:datetime]))
275
- else
276
- set_text(value)
277
- end
256
+ return set_text(value) unless value.respond_to?(:to_time)
257
+ # TODO: this would be better if locale can be detected and correct keystrokes sent
258
+ update_value_js(value.to_time.strftime('%Y-%m-%dT%H:%M'))
259
+ end
260
+
261
+ def update_value_js(value)
262
+ driver.execute_script(<<-JS, self, value)
263
+ if (document.activeElement !== arguments[0]){
264
+ arguments[0].focus();
265
+ }
266
+ if (arguments[0].value != arguments[1]) {
267
+ arguments[0].value = arguments[1]
268
+ arguments[0].dispatchEvent(new InputEvent('input'));
269
+ arguments[0].dispatchEvent(new Event('change', { bubbles: true }));
270
+ }
271
+ JS
278
272
  end
279
273
 
280
274
  def set_file(value) # rubocop:disable Naming/AccessorMethodName