capybara 3.0.3 → 3.1.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 (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