capybara 3.33.0 → 3.34.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +44 -15
  3. data/README.md +0 -2
  4. data/lib/capybara.rb +1 -1
  5. data/lib/capybara/config.rb +4 -6
  6. data/lib/capybara/driver/base.rb +4 -0
  7. data/lib/capybara/helpers.rb +25 -1
  8. data/lib/capybara/minitest.rb +2 -2
  9. data/lib/capybara/minitest/spec.rb +14 -11
  10. data/lib/capybara/node/actions.rb +1 -2
  11. data/lib/capybara/node/element.rb +1 -5
  12. data/lib/capybara/node/finders.rb +7 -6
  13. data/lib/capybara/node/matchers.rb +7 -5
  14. data/lib/capybara/node/simple.rb +5 -1
  15. data/lib/capybara/queries/ancestor_query.rb +1 -1
  16. data/lib/capybara/queries/current_path_query.rb +14 -4
  17. data/lib/capybara/queries/selector_query.rb +8 -9
  18. data/lib/capybara/queries/sibling_query.rb +1 -1
  19. data/lib/capybara/queries/text_query.rb +2 -2
  20. data/lib/capybara/rack_test/browser.rb +7 -3
  21. data/lib/capybara/rack_test/driver.rb +1 -0
  22. data/lib/capybara/rack_test/form.rb +1 -1
  23. data/lib/capybara/rack_test/node.rb +1 -1
  24. data/lib/capybara/registration_container.rb +3 -3
  25. data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
  26. data/lib/capybara/registrations/servers.rb +1 -0
  27. data/lib/capybara/result.rb +3 -7
  28. data/lib/capybara/rspec.rb +2 -0
  29. data/lib/capybara/rspec/matcher_proxies.rb +1 -1
  30. data/lib/capybara/rspec/matchers.rb +7 -6
  31. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  32. data/lib/capybara/rspec/matchers/match_style.rb +5 -0
  33. data/lib/capybara/selector/definition.rb +6 -5
  34. data/lib/capybara/selector/definition/button.rb +7 -5
  35. data/lib/capybara/selector/definition/css.rb +1 -1
  36. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  37. data/lib/capybara/selector/definition/element.rb +2 -1
  38. data/lib/capybara/selector/definition/label.rb +1 -1
  39. data/lib/capybara/selector/definition/select.rb +1 -1
  40. data/lib/capybara/selector/definition/table_row.rb +1 -1
  41. data/lib/capybara/selector/filter_set.rb +2 -2
  42. data/lib/capybara/selector/selector.rb +5 -1
  43. data/lib/capybara/selenium/driver.rb +29 -3
  44. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -3
  45. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +3 -3
  46. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
  47. data/lib/capybara/selenium/extensions/find.rb +3 -3
  48. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  49. data/lib/capybara/selenium/node.rb +6 -3
  50. data/lib/capybara/selenium/nodes/chrome_node.rb +20 -2
  51. data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
  52. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  53. data/lib/capybara/selenium/patches/logs.rb +4 -4
  54. data/lib/capybara/server/animation_disabler.rb +3 -2
  55. data/lib/capybara/server/middleware.rb +4 -2
  56. data/lib/capybara/session.rb +20 -11
  57. data/lib/capybara/session/matchers.rb +11 -11
  58. data/lib/capybara/spec/public/test.js +6 -1
  59. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  60. data/lib/capybara/spec/session/check_spec.rb +6 -0
  61. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  62. data/lib/capybara/spec/session/has_button_spec.rb +2 -0
  63. data/lib/capybara/spec/session/has_css_spec.rb +2 -1
  64. data/lib/capybara/spec/session/has_current_path_spec.rb +13 -0
  65. data/lib/capybara/spec/session/has_text_spec.rb +0 -11
  66. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  67. data/lib/capybara/spec/session/node_spec.rb +22 -2
  68. data/lib/capybara/spec/session/refresh_spec.rb +1 -0
  69. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  70. data/lib/capybara/spec/spec_helper.rb +11 -12
  71. data/lib/capybara/spec/test_app.rb +8 -3
  72. data/lib/capybara/spec/views/form.erb +18 -0
  73. data/lib/capybara/spec/views/with_animation.erb +8 -0
  74. data/lib/capybara/spec/views/with_js.erb +1 -0
  75. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  76. data/lib/capybara/version.rb +1 -1
  77. data/lib/capybara/window.rb +3 -7
  78. data/spec/basic_node_spec.rb +9 -8
  79. data/spec/dsl_spec.rb +1 -1
  80. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  81. data/spec/minitest_spec.rb +2 -1
  82. data/spec/rack_test_spec.rb +15 -5
  83. data/spec/rspec/features_spec.rb +3 -1
  84. data/spec/rspec/scenarios_spec.rb +4 -0
  85. data/spec/rspec/shared_spec_matchers.rb +2 -2
  86. data/spec/rspec_spec.rb +4 -0
  87. data/spec/selector_spec.rb +1 -0
  88. data/spec/server_spec.rb +15 -0
  89. data/spec/shared_selenium_session.rb +60 -1
  90. metadata +22 -23
  91. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -18,7 +18,8 @@ Capybara.add_selector(:element, locator_type: [String, Symbol]) do
18
18
  end
19
19
 
20
20
  describe_expression_filters do |**options|
21
- booleans, values = options.partition { |_k, v| [true, false].include? v }.map(&:to_h)
21
+ boolean_values = [true, false]
22
+ booleans, values = options.partition { |_k, v| boolean_values.include? v }.map(&:to_h)
22
23
  desc = describe_all_expression_filters(**values)
23
24
  desc + booleans.map do |k, v|
24
25
  v ? " with #{k} attribute" : "without #{k} attribute"
@@ -53,7 +53,7 @@ Capybara.add_selector(:label, locator_type: [String, Symbol]) do
53
53
  end
54
54
  end
55
55
  describe_node_filters do |**options|
56
- " for element #{options[:for]}" if options[:for]&.is_a?(Capybara::Node::Element)
56
+ " for element #{options[:for]}" if options[:for].is_a?(Capybara::Node::Element)
57
57
  end
58
58
 
59
59
  def labelable_elements
@@ -33,7 +33,7 @@ Capybara.add_selector(:select, locator_type: [String, Symbol]) do
33
33
 
34
34
  expression_filter(:with_options) do |expr, options|
35
35
  options.inject(expr) do |xpath, option|
36
- xpath[expression_for(:option, option)]
36
+ xpath.where(expression_for(:option, option))
37
37
  end
38
38
  end
39
39
 
@@ -14,7 +14,7 @@ Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
14
14
  else
15
15
  initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
16
16
  tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }
17
- .reduce { |xp, cell| xp[cell] }
17
+ .reduce { |xp, cell| xp.where(cell) }
18
18
  xpath[initial_td[tds]]
19
19
  end
20
20
  end
@@ -12,7 +12,7 @@ module Capybara
12
12
  @node_filters = {}
13
13
  @expression_filters = {}
14
14
  @descriptions = Hash.new { |hsh, key| hsh[key] = [] }
15
- instance_eval(&block)
15
+ instance_eval(&block) if block
16
16
  end
17
17
 
18
18
  def node_filter(names, *types, **options, &block)
@@ -49,7 +49,7 @@ module Capybara
49
49
  end
50
50
 
51
51
  def descriptions
52
- warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
52
+ Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
53
53
  [undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
54
54
  end
55
55
 
@@ -132,7 +132,11 @@ module Capybara
132
132
  attr_matchers |= XPath.attr(test_id) == locator if test_id
133
133
 
134
134
  locate_xpath = locate_xpath[attr_matchers]
135
- locate_xpath + XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
135
+ locate_xpath + locate_label(locator).descendant(xpath)
136
+ end
137
+
138
+ def locate_label(locator)
139
+ XPath.descendant(:label)[XPath.string.n.is(locator)]
136
140
  end
137
141
 
138
142
  def find_by_attr(attribute, value)
@@ -21,11 +21,28 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
21
21
  require 'capybara/selenium/patches/atoms'
22
22
  require 'capybara/selenium/patches/is_displayed'
23
23
  require 'capybara/selenium/patches/action_pauser'
24
- if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0')
24
+
25
+ # Look up the version of `selenium-webdriver` to
26
+ # see if it's a version we support.
27
+ #
28
+ # By default, we use Gem.loaded_specs to determine
29
+ # the version number. However, in some cases, such
30
+ # as when loading `selenium-webdriver` outside of
31
+ # Rubygems, we fall back to referencing
32
+ # Selenium::WebDriver::VERSION. Ideally we'd
33
+ # use the constant in all cases, but earlier versions
34
+ # of `selenium-webdriver` didn't provide the constant.
35
+ selenium_webdriver_version =
36
+ if Gem.loaded_specs['selenium-webdriver']
37
+ Gem.loaded_specs['selenium-webdriver'].version
38
+ else
39
+ Gem::Version.new(Selenium::WebDriver::VERSION)
40
+ end
41
+ if selenium_webdriver_version < Gem::Version.new('3.5.0')
25
42
  warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade."
26
43
  end
27
44
  rescue LoadError => e
28
- raise e unless e.message.match?(/selenium-webdriver/)
45
+ raise e unless e.message.include?('selenium-webdriver')
29
46
 
30
47
  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
48
  end
@@ -58,6 +75,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
58
75
  end
59
76
 
60
77
  def initialize(app, **options)
78
+ super()
61
79
  self.class.load_selenium
62
80
  @app = app
63
81
  @browser = nil
@@ -86,7 +104,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
86
104
  def html
87
105
  browser.page_source
88
106
  rescue Selenium::WebDriver::Error::JavascriptError => e
89
- raise unless e.message.match?(/documentElement is null/)
107
+ raise unless e.message.include?('documentElement is null')
90
108
  end
91
109
 
92
110
  def title
@@ -115,6 +133,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
115
133
  unwrap_script_result(result)
116
134
  end
117
135
 
136
+ def send_keys(*args)
137
+ active_element.send_keys(*args)
138
+ end
139
+
118
140
  def save_screenshot(path, **_options)
119
141
  browser.save_screenshot(path)
120
142
  end
@@ -437,6 +459,10 @@ private
437
459
  browser
438
460
  end
439
461
 
462
+ def active_element
463
+ browser.switch_to.active_element
464
+ end
465
+
440
466
  def build_node(native_node, initial_cache = {})
441
467
  ::Capybara::Selenium::Node.new(self, native_node, initial_cache)
442
468
  end
@@ -7,7 +7,7 @@ module Capybara::Selenium::Driver::ChromeDriver
7
7
  def self.extended(base)
8
8
  bridge = base.send(:bridge)
9
9
  bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:log)
10
- bridge.extend Capybara::Selenium::IsDisplayed unless bridge.commands(:is_element_displayed)
10
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
11
11
  base.options[:native_displayed] = false if base.options[:native_displayed].nil?
12
12
  end
13
13
 
@@ -15,7 +15,7 @@ module Capybara::Selenium::Driver::ChromeDriver
15
15
  within_given_window(handle) do
16
16
  super
17
17
  rescue NoMethodError => e
18
- raise unless e.message.match?(/full_screen_window/)
18
+ raise unless e.message.include?('full_screen_window')
19
19
 
20
20
  result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
21
21
  result['value']
@@ -25,7 +25,7 @@ module Capybara::Selenium::Driver::ChromeDriver
25
25
  def resize_window_to(handle, width, height)
26
26
  super
27
27
  rescue Selenium::WebDriver::Error::UnknownError => e
28
- raise unless e.message.match?(/failed to change window state/)
28
+ raise unless e.message.include?('failed to change window state')
29
29
 
30
30
  # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
31
31
  # and raises unnecessary error. Wait a bit and try again.
@@ -5,7 +5,7 @@ require 'capybara/selenium/nodes/edge_node'
5
5
  module Capybara::Selenium::Driver::EdgeDriver
6
6
  def self.extended(base)
7
7
  bridge = base.send(:bridge)
8
- bridge.extend Capybara::Selenium::IsDisplayed unless bridge.commands(:is_element_displayed)
8
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
9
9
  base.options[:native_displayed] = false if base.options[:native_displayed].nil?
10
10
  end
11
11
 
@@ -15,7 +15,7 @@ module Capybara::Selenium::Driver::EdgeDriver
15
15
  within_given_window(handle) do
16
16
  super
17
17
  rescue NoMethodError => e
18
- raise unless e.message.match?(/full_screen_window/)
18
+ raise unless e.message.include?('full_screen_window')
19
19
 
20
20
  result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
21
21
  result['value']
@@ -25,7 +25,7 @@ module Capybara::Selenium::Driver::EdgeDriver
25
25
  def resize_window_to(handle, width, height)
26
26
  super
27
27
  rescue Selenium::WebDriver::Error::UnknownError => e
28
- raise unless e.message.match?(/failed to change window state/)
28
+ raise unless e.message.include?('failed to change window state')
29
29
 
30
30
  # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
31
31
  # and raises unnecessary error. Wait a bit and try again.
@@ -6,7 +6,7 @@ module Capybara::Selenium::Driver::FirefoxDriver
6
6
  def self.extended(driver)
7
7
  driver.extend Capybara::Selenium::Driver::W3CFirefoxDriver if w3c?(driver)
8
8
  bridge = driver.send(:bridge)
9
- bridge.extend Capybara::Selenium::IsDisplayed unless bridge.commands(:is_element_displayed)
9
+ bridge.extend Capybara::Selenium::IsDisplayed unless bridge.send(:commands, :is_element_displayed)
10
10
  end
11
11
 
12
12
  def self.w3c?(driver)
@@ -100,9 +100,9 @@ module Capybara
100
100
  def is_displayed_atom # rubocop:disable Naming/PredicateName
101
101
  @@is_displayed_atom ||= begin # rubocop:disable Style/ClassVars
102
102
  browser.send(:bridge).send(:read_atom, 'isDisplayed')
103
- rescue StandardError
104
- # If the atom doesn't exist or other error
105
- ''
103
+ rescue StandardError
104
+ # If the atom doesn't exist or other error
105
+ ''
106
106
  end
107
107
  end
108
108
  end
@@ -45,20 +45,18 @@ module Capybara
45
45
  JS
46
46
  end
47
47
 
48
+ SCROLL_POSITIONS = {
49
+ top: '0',
50
+ bottom: 'arguments[0].scrollHeight',
51
+ center: '(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
52
+ }.freeze
53
+
48
54
  def scroll_to_location(location)
49
- scroll_y = case location
50
- when :top
51
- '0'
52
- when :bottom
53
- 'arguments[0].scrollHeight'
54
- when :center
55
- '(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
56
- end
57
55
  driver.execute_script <<~JS, self
58
56
  if (arguments[0].scrollTo){
59
- arguments[0].scrollTo(0, #{scroll_y});
57
+ arguments[0].scrollTo(0, #{SCROLL_POSITIONS[location]});
60
58
  } else {
61
- arguments[0].scrollTop = #{scroll_y};
59
+ arguments[0].scrollTop = #{SCROLL_POSITIONS[location]};
62
60
  }
63
61
  JS
64
62
  end
@@ -120,7 +120,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
120
120
  end
121
121
  rescue StandardError => e
122
122
  if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
123
- e.message.match?(/Other element would receive the click/)
123
+ e.message.include?('Other element would receive the click')
124
124
  scroll_to_center
125
125
  end
126
126
 
@@ -464,8 +464,8 @@ private
464
464
  end
465
465
  end
466
466
 
467
- def each_key(keys)
468
- normalize_keys(keys).each { |key| yield(key) }
467
+ def each_key(keys, &block)
468
+ normalize_keys(keys).each(&block)
469
469
  end
470
470
 
471
471
  def find_context
@@ -493,6 +493,9 @@ private
493
493
  var xpath = '';
494
494
  var pos, tempitem2;
495
495
 
496
+ if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
497
+ return "(: Shadow DOM element - no XPath :)";
498
+ };
496
499
  while(el !== xml.documentElement) {
497
500
  pos = 0;
498
501
  tempitem2 = el;
@@ -42,7 +42,7 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
42
42
  raise
43
43
  rescue ::Selenium::WebDriver::Error::WebDriverError => e
44
44
  # chromedriver 74 (at least on mac) raises the wrong error for this
45
- if e.message.match?(/element click intercepted/)
45
+ if e.message.include?('element click intercepted')
46
46
  raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message
47
47
  end
48
48
 
@@ -73,6 +73,24 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
73
73
  end
74
74
  end
75
75
 
76
+ def send_keys(*args)
77
+ args.chunk { |inp| inp.is_a?(String) && inp.match?(/\p{Emoji Presentation}/) }
78
+ .each do |contains_emoji, inputs|
79
+ if contains_emoji
80
+ inputs.join.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
81
+ .each do |emoji, clusters|
82
+ if emoji
83
+ driver.send(:execute_cdp, 'Input.insertText', text: clusters.join)
84
+ else
85
+ super(clusters.join)
86
+ end
87
+ end
88
+ else
89
+ super(*inputs)
90
+ end
91
+ end
92
+ end
93
+
76
94
  private
77
95
 
78
96
  def perform_legacy_drag(element, drop_modifiers)
@@ -92,7 +110,7 @@ private
92
110
  end
93
111
  end
94
112
 
95
- def browser_version(to_float = true)
113
+ def browser_version(to_float: true)
96
114
  caps = capabilities
97
115
  ver = (caps[:browser_version] || caps[:version])
98
116
  ver = ver.to_f if to_float
@@ -43,7 +43,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
43
43
  return '' unless visible?
44
44
 
45
45
  vis_text = driver.execute_script('return arguments[0].innerText', self)
46
- vis_text.gsub(/\ +/, ' ')
46
+ vis_text.squeeze(' ')
47
47
  .gsub(/[\ \n]*\n[\ \n]*/, "\n")
48
48
  .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
49
49
  .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
@@ -6,10 +6,10 @@ private
6
6
  def read_atom(function)
7
7
  @atoms ||= Hash.new do |hash, key|
8
8
  hash[key] = begin
9
- File.read(File.expand_path("../../atoms/#{key}.min.js", __FILE__))
10
- rescue Errno::ENOENT
11
- super
12
- end
9
+ File.read(File.expand_path("../../atoms/#{key}.min.js", __FILE__))
10
+ rescue Errno::ENOENT
11
+ super
12
+ end
13
13
  end
14
14
  @atoms[function]
15
15
  end
@@ -27,10 +27,10 @@ module Capybara
27
27
 
28
28
  def log(type)
29
29
  data = begin
30
- execute :get_log, {}, type: type.to_s
31
- rescue ::Selenium::WebDriver::Error::UnknownCommandError
32
- execute :get_log_legacy, {}, type: type.to_s
33
- end
30
+ execute :get_log, {}, type: type.to_s
31
+ rescue ::Selenium::WebDriver::Error::UnknownCommandError
32
+ execute :get_log_legacy, {}, type: type.to_s
33
+ end
34
34
 
35
35
  Array(data).map do |l|
36
36
  ::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
@@ -40,16 +40,17 @@ module Capybara
40
40
  end
41
41
 
42
42
  def insert_disable(html)
43
- html.sub(%r{(</head>)}, disable_markup + '\\1')
43
+ html.sub(%r{(</head>)}, "#{disable_markup}\\1")
44
44
  end
45
45
 
46
46
  DISABLE_MARKUP_TEMPLATE = <<~HTML
47
47
  <script defer>(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);</script>
48
48
  <style>
49
- %<selector>s, %<selector>s::before, %<selector>s::after {
49
+ %<selector>s, %<selector>s::before, %<selector>s::after {
50
50
  transition: none !important;
51
51
  animation-duration: 0s !important;
52
52
  animation-delay: 0s !important;
53
+ scroll-behavior: auto !important;
53
54
  }
54
55
  </style>
55
56
  HTML
@@ -53,14 +53,16 @@ module Capybara
53
53
  if env['PATH_INFO'] == '/__identify__'
54
54
  [200, {}, [@app.object_id.to_s]]
55
55
  else
56
- @counter.increment(env['REQUEST_URI'])
56
+ request_uri = env['REQUEST_URI']
57
+ @counter.increment(request_uri)
58
+
57
59
  begin
58
60
  @extended_app.call(env)
59
61
  rescue *@server_errors => e
60
62
  @error ||= e
61
63
  raise e
62
64
  ensure
63
- @counter.decrement(env['REQUEST_URI'])
65
+ @counter.decrement(request_uri)
64
66
  end
65
67
  end
66
68
  end
@@ -58,7 +58,7 @@ module Capybara
58
58
  ].freeze
59
59
  SESSION_METHODS = %i[
60
60
  body html source current_url current_host current_path
61
- execute_script evaluate_script visit refresh go_back go_forward
61
+ execute_script evaluate_script visit refresh go_back go_forward send_keys
62
62
  within within_element within_fieldset within_table within_frame switch_to_frame
63
63
  current_window windows open_new_window switch_to_window within_window window_opened_by
64
64
  save_page save_and_open_page save_screenshot
@@ -303,6 +303,14 @@ module Capybara
303
303
  driver.go_forward
304
304
  end
305
305
 
306
+ ##
307
+ # @!method send_keys
308
+ # @see Capybara::Node::Element#send_keys
309
+ #
310
+ def send_keys(*args, **kw_args)
311
+ driver.send_keys(*args, **kw_args)
312
+ end
313
+
306
314
  ##
307
315
  #
308
316
  # Executes the given block within the context of a node. {#within} takes the
@@ -355,8 +363,8 @@ module Capybara
355
363
  #
356
364
  # @param [String] locator Id or legend of the fieldset
357
365
  #
358
- def within_fieldset(locator)
359
- within(:fieldset, locator) { yield }
366
+ def within_fieldset(locator, &block)
367
+ within(:fieldset, locator, &block)
360
368
  end
361
369
 
362
370
  ##
@@ -365,8 +373,8 @@ module Capybara
365
373
  #
366
374
  # @param [String] locator Id or caption of the table
367
375
  #
368
- def within_table(locator)
369
- within(:table, locator) { yield }
376
+ def within_table(locator, &block)
377
+ within(:table, locator, &block)
370
378
  end
371
379
 
372
380
  ##
@@ -398,8 +406,9 @@ module Capybara
398
406
  driver.switch_to_frame(:parent)
399
407
  when :top
400
408
  idx = scopes.index(:frame)
409
+ top_level_scopes = [:frame, nil]
401
410
  if idx
402
- if scopes.slice(idx..-1).any? { |scope| ![:frame, nil].include?(scope) }
411
+ if scopes.slice(idx..-1).any? { |scope| !top_level_scopes.include?(scope) }
403
412
  raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
404
413
  '`within` block.'
405
414
  end
@@ -488,8 +497,8 @@ module Capybara
488
497
  # @raise [ArgumentError] if both or neither arguments were provided
489
498
  #
490
499
  def switch_to_window(window = nil, **options, &window_locator)
491
- raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && block_given?
492
- raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !block_given?
500
+ raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
501
+ raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
493
502
 
494
503
  unless scopes.last.nil?
495
504
  raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
@@ -738,7 +747,7 @@ module Capybara
738
747
  # @param [Hash] options a customizable set of options
739
748
  #
740
749
  def save_and_open_screenshot(path = nil, **options)
741
- save_screenshot(path, **options).tap { |s_path| open_file(s_path) } # rubocop:disable Lint/Debugger
750
+ save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
742
751
  end
743
752
 
744
753
  def document
@@ -788,7 +797,7 @@ module Capybara
788
797
  #
789
798
  # Yield a block using a specific maximum wait time.
790
799
  #
791
- def using_wait_time(seconds)
800
+ def using_wait_time(seconds, &block)
792
801
  if Capybara.threadsafe
793
802
  begin
794
803
  previous_wait_time = config.default_max_wait_time
@@ -798,7 +807,7 @@ module Capybara
798
807
  config.default_max_wait_time = previous_wait_time
799
808
  end
800
809
  else
801
- Capybara.using_wait_time(seconds) { yield }
810
+ Capybara.using_wait_time(seconds, &block)
802
811
  end
803
812
  end
804
813