capybara 3.23.0 → 3.35.3

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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +264 -11
  3. data/README.md +10 -6
  4. data/lib/capybara.rb +20 -8
  5. data/lib/capybara/config.rb +10 -8
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +4 -0
  8. data/lib/capybara/driver/node.rb +4 -0
  9. data/lib/capybara/dsl.rb +10 -2
  10. data/lib/capybara/helpers.rb +28 -2
  11. data/lib/capybara/minitest.rb +232 -144
  12. data/lib/capybara/minitest/spec.rb +156 -97
  13. data/lib/capybara/node/actions.rb +36 -36
  14. data/lib/capybara/node/base.rb +6 -6
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +3 -3
  17. data/lib/capybara/node/element.rb +77 -33
  18. data/lib/capybara/node/finders.rb +24 -17
  19. data/lib/capybara/node/matchers.rb +79 -64
  20. data/lib/capybara/node/simple.rb +11 -4
  21. data/lib/capybara/queries/ancestor_query.rb +6 -10
  22. data/lib/capybara/queries/base_query.rb +2 -1
  23. data/lib/capybara/queries/current_path_query.rb +14 -4
  24. data/lib/capybara/queries/selector_query.rb +259 -23
  25. data/lib/capybara/queries/sibling_query.rb +5 -11
  26. data/lib/capybara/queries/style_query.rb +1 -1
  27. data/lib/capybara/queries/text_query.rb +13 -1
  28. data/lib/capybara/rack_test/browser.rb +13 -4
  29. data/lib/capybara/rack_test/driver.rb +2 -1
  30. data/lib/capybara/rack_test/form.rb +2 -2
  31. data/lib/capybara/rack_test/node.rb +42 -6
  32. data/lib/capybara/registration_container.rb +44 -0
  33. data/lib/capybara/registrations/drivers.rb +18 -12
  34. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  35. data/lib/capybara/registrations/servers.rb +9 -2
  36. data/lib/capybara/result.rb +39 -19
  37. data/lib/capybara/rspec.rb +2 -0
  38. data/lib/capybara/rspec/matcher_proxies.rb +5 -5
  39. data/lib/capybara/rspec/matchers.rb +97 -74
  40. data/lib/capybara/rspec/matchers/base.rb +19 -6
  41. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  42. data/lib/capybara/rspec/matchers/have_ancestor.rb +5 -7
  43. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  44. data/lib/capybara/rspec/matchers/have_selector.rb +15 -10
  45. data/lib/capybara/rspec/matchers/have_sibling.rb +4 -7
  46. data/lib/capybara/rspec/matchers/have_text.rb +4 -7
  47. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  48. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  49. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  50. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  51. data/lib/capybara/selector.rb +46 -19
  52. data/lib/capybara/selector/builders/css_builder.rb +10 -6
  53. data/lib/capybara/selector/builders/xpath_builder.rb +4 -2
  54. data/lib/capybara/selector/css.rb +1 -1
  55. data/lib/capybara/selector/definition.rb +13 -11
  56. data/lib/capybara/selector/definition/button.rb +32 -15
  57. data/lib/capybara/selector/definition/checkbox.rb +2 -2
  58. data/lib/capybara/selector/definition/css.rb +3 -1
  59. data/lib/capybara/selector/definition/datalist_input.rb +2 -2
  60. data/lib/capybara/selector/definition/datalist_option.rb +1 -1
  61. data/lib/capybara/selector/definition/element.rb +3 -2
  62. data/lib/capybara/selector/definition/field.rb +1 -1
  63. data/lib/capybara/selector/definition/file_field.rb +1 -1
  64. data/lib/capybara/selector/definition/fillable_field.rb +2 -2
  65. data/lib/capybara/selector/definition/label.rb +5 -3
  66. data/lib/capybara/selector/definition/link.rb +8 -0
  67. data/lib/capybara/selector/definition/option.rb +1 -1
  68. data/lib/capybara/selector/definition/radio_button.rb +2 -2
  69. data/lib/capybara/selector/definition/select.rb +33 -14
  70. data/lib/capybara/selector/definition/table.rb +6 -3
  71. data/lib/capybara/selector/definition/table_row.rb +2 -2
  72. data/lib/capybara/selector/filter_set.rb +13 -11
  73. data/lib/capybara/selector/filters/base.rb +6 -1
  74. data/lib/capybara/selector/filters/locator_filter.rb +1 -1
  75. data/lib/capybara/selector/regexp_disassembler.rb +7 -0
  76. data/lib/capybara/selector/selector.rb +13 -3
  77. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  78. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -1
  79. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  80. data/lib/capybara/selenium/atoms/src/isDisplayed.js +10 -10
  81. data/lib/capybara/selenium/driver.rb +86 -24
  82. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +24 -21
  83. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +21 -19
  84. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +17 -1
  85. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +0 -4
  86. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  87. data/lib/capybara/selenium/extensions/find.rb +37 -26
  88. data/lib/capybara/selenium/extensions/html5_drag.rb +55 -11
  89. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  90. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  91. data/lib/capybara/selenium/logger_suppressor.rb +8 -2
  92. data/lib/capybara/selenium/node.rb +160 -40
  93. data/lib/capybara/selenium/nodes/chrome_node.rb +72 -12
  94. data/lib/capybara/selenium/nodes/edge_node.rb +32 -14
  95. data/lib/capybara/selenium/nodes/firefox_node.rb +28 -32
  96. data/lib/capybara/selenium/nodes/safari_node.rb +5 -29
  97. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  98. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  99. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  100. data/lib/capybara/selenium/patches/logs.rb +32 -7
  101. data/lib/capybara/server.rb +19 -3
  102. data/lib/capybara/server/animation_disabler.rb +8 -3
  103. data/lib/capybara/server/checker.rb +1 -1
  104. data/lib/capybara/server/middleware.rb +22 -10
  105. data/lib/capybara/session.rb +66 -40
  106. data/lib/capybara/session/config.rb +11 -3
  107. data/lib/capybara/session/matchers.rb +11 -11
  108. data/lib/capybara/spec/public/offset.js +6 -0
  109. data/lib/capybara/spec/public/test.js +75 -7
  110. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  111. data/lib/capybara/spec/session/all_spec.rb +60 -5
  112. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  113. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  114. data/lib/capybara/spec/session/check_spec.rb +6 -0
  115. data/lib/capybara/spec/session/click_button_spec.rb +16 -0
  116. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  117. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  118. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  119. data/lib/capybara/spec/session/find_spec.rb +55 -0
  120. data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -0
  121. data/lib/capybara/spec/session/has_button_spec.rb +51 -0
  122. data/lib/capybara/spec/session/has_css_spec.rb +26 -4
  123. data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
  124. data/lib/capybara/spec/session/has_field_spec.rb +34 -0
  125. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  126. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  127. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  128. data/lib/capybara/spec/session/has_text_spec.rb +30 -0
  129. data/lib/capybara/spec/session/html_spec.rb +1 -1
  130. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  131. data/lib/capybara/spec/session/node_spec.rb +394 -9
  132. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  133. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  134. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  135. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -15
  136. data/lib/capybara/spec/session/selectors_spec.rb +16 -3
  137. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  138. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  139. data/lib/capybara/spec/session/window/window_spec.rb +8 -8
  140. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  141. data/lib/capybara/spec/spec_helper.rb +14 -14
  142. data/lib/capybara/spec/test_app.rb +27 -21
  143. data/lib/capybara/spec/views/form.erb +47 -4
  144. data/lib/capybara/spec/views/offset.erb +32 -0
  145. data/lib/capybara/spec/views/spatial.erb +31 -0
  146. data/lib/capybara/spec/views/with_animation.erb +37 -1
  147. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  148. data/lib/capybara/spec/views/with_html.erb +24 -2
  149. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  150. data/lib/capybara/spec/views/with_js.erb +4 -1
  151. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  152. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  153. data/lib/capybara/version.rb +1 -1
  154. data/lib/capybara/window.rb +3 -7
  155. data/spec/basic_node_spec.rb +15 -14
  156. data/spec/capybara_spec.rb +28 -28
  157. data/spec/dsl_spec.rb +16 -3
  158. data/spec/filter_set_spec.rb +5 -5
  159. data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
  160. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  161. data/spec/minitest_spec.rb +3 -2
  162. data/spec/minitest_spec_spec.rb +46 -46
  163. data/spec/rack_test_spec.rb +38 -15
  164. data/spec/regexp_dissassembler_spec.rb +52 -38
  165. data/spec/result_spec.rb +43 -32
  166. data/spec/rspec/features_spec.rb +4 -1
  167. data/spec/rspec/scenarios_spec.rb +4 -0
  168. data/spec/rspec/shared_spec_matchers.rb +68 -56
  169. data/spec/rspec_spec.rb +9 -5
  170. data/spec/selector_spec.rb +32 -17
  171. data/spec/selenium_spec_chrome.rb +78 -11
  172. data/spec/selenium_spec_chrome_remote.rb +23 -6
  173. data/spec/selenium_spec_edge.rb +15 -12
  174. data/spec/selenium_spec_firefox.rb +24 -19
  175. data/spec/selenium_spec_firefox_remote.rb +0 -8
  176. data/spec/selenium_spec_ie.rb +1 -6
  177. data/spec/server_spec.rb +106 -44
  178. data/spec/session_spec.rb +5 -5
  179. data/spec/shared_selenium_node.rb +56 -2
  180. data/spec/shared_selenium_session.rb +122 -15
  181. data/spec/spec_helper.rb +2 -2
  182. metadata +63 -17
  183. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -18,14 +18,16 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
18
18
  # In Chrome 75+ files are appended (due to WebDriver spec - why?) so we have to clear here if its multiple and already set
19
19
  if chrome_edge?
20
20
  driver.execute_script(<<~JS, self)
21
- if (arguments[0].multiple && (arguments[0].files.length > 0)){
21
+ if (arguments[0].multiple && arguments[0].files.length){
22
22
  arguments[0].value = null;
23
23
  }
24
24
  JS
25
25
  end
26
26
  super
27
27
  rescue *file_errors => e
28
- raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
28
+ if e.message.match?(/File not found : .+\n.+/m)
29
+ raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload"
30
+ end
29
31
 
30
32
  raise
31
33
  end
@@ -36,14 +38,16 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
36
38
  html5_drop(*args)
37
39
  end
38
40
 
39
- # def click(*)
40
- # super
41
- # rescue ::Selenium::WebDriver::Error::WebDriverError => e
42
- # # chromedriver 74 (at least on mac) raises the wrong error for this
43
- # raise ::Selenium::WebDriver::Error::ElementClickInterceptedError, e.message if e.message.match?(/element click intercepted/)
44
- #
45
- # raise
46
- # end
41
+ def click(*)
42
+ super
43
+ rescue Selenium::WebDriver::Error::InvalidArgumentError => e
44
+ tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
45
+ if tag_name == 'input' && type == 'file'
46
+ raise Selenium::WebDriver::Error::InvalidArgumentError, "EdgeChrome can't click on file inputs.\n#{e.message}"
47
+ end
48
+
49
+ raise
50
+ end
47
51
 
48
52
  def disabled?
49
53
  return super unless chrome_edge?
@@ -61,6 +65,18 @@ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
61
65
  click unless selected_or_disabled
62
66
  end
63
67
 
68
+ def visible?
69
+ return super unless chrome_edge? && native_displayed?
70
+
71
+ begin
72
+ bridge.send(:execute, :is_element_displayed, id: native.ref)
73
+ rescue Selenium::WebDriver::Error::UnknownCommandError
74
+ # If the is_element_displayed command is unknown, no point in trying again
75
+ driver.options[:native_displayed] = false
76
+ super
77
+ end
78
+ end
79
+
64
80
  private
65
81
 
66
82
  def file_errors
@@ -69,10 +85,6 @@ private
69
85
  end
70
86
  end
71
87
 
72
- def bridge
73
- driver.browser.send(:bridge)
74
- end
75
-
76
88
  def browser_version
77
89
  @browser_version ||= begin
78
90
  caps = driver.browser.capabilities
@@ -83,4 +95,10 @@ private
83
95
  def chrome_edge?
84
96
  browser_version >= 75
85
97
  end
98
+
99
+ def native_displayed?
100
+ (driver.options[:native_displayed] != false) &&
101
+ # chromedriver_supports_displayed_endpoint? &&
102
+ (!ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS'])
103
+ end
86
104
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'capybara/selenium/extensions/html5_drag'
4
+ require 'capybara/selenium/extensions/file_input_click_emulation'
4
5
 
5
6
  class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
6
7
  include Html5Drag
8
+ include FileInputClickEmulation
7
9
 
8
10
  def click(keys = [], **options)
9
11
  super
@@ -12,7 +14,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
12
14
  warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\
13
15
  'see https://github.com/mozilla/geckodriver/issues/1228. Your test should probably be '\
14
16
  'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
15
- return find_css('th:first-child,td:first-child')[0].click(keys, options)
17
+ return find_css('th:first-child,td:first-child')[0].click(keys, **options)
16
18
  end
17
19
  raise
18
20
  end
@@ -24,7 +26,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
24
26
  def set_file(value) # rubocop:disable Naming/AccessorMethodName
25
27
  # By default files are appended so we have to clear here if its multiple and already set
26
28
  driver.execute_script(<<~JS, self)
27
- if (arguments[0].multiple && (arguments[0].files.length > 0)){
29
+ if (arguments[0].multiple && arguments[0].files.length){
28
30
  arguments[0].value = null;
29
31
  }
30
32
  JS
@@ -38,11 +40,16 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
38
40
  path_names.each { |path| native.send_keys(path) }
39
41
  end
40
42
 
43
+ def focused?
44
+ driver.evaluate_script('arguments[0] == document.activeElement', self)
45
+ end
46
+
41
47
  def send_keys(*args)
42
48
  # https://github.com/mozilla/geckodriver/issues/846
43
- return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array }
49
+ return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?(Array)
50
+
51
+ native.click unless focused?
44
52
 
45
- native.click
46
53
  _send_keys(args).perform
47
54
  end
48
55
 
@@ -65,9 +72,25 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
65
72
  click unless selected_or_disabled
66
73
  end
67
74
 
75
+ def visible?
76
+ return super unless native_displayed?
77
+
78
+ begin
79
+ bridge.send(:execute, :is_element_displayed, id: native.ref)
80
+ rescue Selenium::WebDriver::Error::UnknownCommandError
81
+ # If the is_element_displayed command is unknown, no point in trying again
82
+ driver.options[:native_displayed] = false
83
+ super
84
+ end
85
+ end
86
+
68
87
  private
69
88
 
70
- def click_with_options(click_options)
89
+ def native_displayed?
90
+ (driver.options[:native_displayed] != false) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
91
+ end
92
+
93
+ def perform_with_options(click_options)
71
94
  # Firefox/marionette has an issue clicking with offset near viewport edge
72
95
  # scroll element to middle just in case
73
96
  scroll_to_center if click_options.coords?
@@ -99,10 +122,6 @@ private
99
122
  actions
100
123
  end
101
124
 
102
- def bridge
103
- driver.browser.send(:bridge)
104
- end
105
-
106
125
  def upload(local_file)
107
126
  return nil unless local_file
108
127
  raise ArgumentError, "You may only upload files: #{local_file.inspect}" unless File.file?(local_file)
@@ -114,27 +133,4 @@ private
114
133
  def browser_version
115
134
  driver.browser.capabilities[:browser_version].to_f
116
135
  end
117
-
118
- class ModifierKeysStack
119
- def initialize
120
- @stack = []
121
- end
122
-
123
- def include?(key)
124
- @stack.flatten.include?(key)
125
- end
126
-
127
- def press(key)
128
- @stack.last.push(key)
129
- end
130
-
131
- def push
132
- @stack.push []
133
- end
134
-
135
- def pop
136
- @stack.pop
137
- end
138
- end
139
- private_constant :ModifierKeysStack
140
136
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # require 'capybara/selenium/extensions/html5_drag'
4
+ require 'capybara/selenium/extensions/modifier_keys_stack'
4
5
 
5
6
  class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
6
7
  # include Html5Drag
@@ -16,7 +17,9 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
16
17
  return find_css('th:first-child,td:first-child')[0].click(keys, options)
17
18
  end
18
19
  raise
19
- rescue ::Selenium::WebDriver::Error::WebDriverError
20
+ rescue ::Selenium::WebDriver::Error::WebDriverError => e
21
+ raise unless e.instance_of? ::Selenium::WebDriver::Error::WebDriverError
22
+
20
23
  # Safari doesn't return a specific error here - assume it's an ElementNotInteractableError
21
24
  raise ::Selenium::WebDriver::Error::ElementNotInteractableError,
22
25
  'Non distinct error raised in #click, translated to ElementNotInteractableError for retry'
@@ -40,7 +43,7 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
40
43
  return '' unless visible?
41
44
 
42
45
  vis_text = driver.execute_script('return arguments[0].innerText', self)
43
- vis_text.gsub(/\ +/, ' ')
46
+ vis_text.squeeze(' ')
44
47
  .gsub(/[\ \n]*\n[\ \n]*/, "\n")
45
48
  .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
46
49
  .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
@@ -87,10 +90,6 @@ class Capybara::Selenium::SafariNode < Capybara::Selenium::Node
87
90
 
88
91
  private
89
92
 
90
- def bridge
91
- driver.browser.send(:bridge)
92
- end
93
-
94
93
  def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
95
94
  case keys
96
95
  when *MODIFIER_KEYS
@@ -116,27 +115,4 @@ private
116
115
  shift left_shift right_shift
117
116
  meta left_meta right_meta
118
117
  command].freeze
119
-
120
- class ModifierKeysStack
121
- def initialize
122
- @stack = []
123
- end
124
-
125
- def include?(key)
126
- @stack.flatten.include?(key)
127
- end
128
-
129
- def press(key)
130
- @stack.last.push(key)
131
- end
132
-
133
- def push
134
- @stack.push []
135
- end
136
-
137
- def pop
138
- @stack.pop
139
- end
140
- end
141
- private_constant :ModifierKeysStack
142
118
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPauser
4
+ def initialize(mouse, keyboard)
5
+ super
6
+ @devices[:pauser] = Pauser.new
7
+ end
8
+
9
+ def pause(duration)
10
+ @actions << [:pauser, :pause, [duration]]
11
+ self
12
+ end
13
+
14
+ class Pauser
15
+ def pause(duration)
16
+ sleep duration
17
+ end
18
+ end
19
+
20
+ private_constant :Pauser
21
+ end
22
+
23
+ if defined?(::Selenium::WebDriver::VERSION) && (::Selenium::WebDriver::VERSION.to_f < 4) &&
24
+ defined?(::Selenium::WebDriver::ActionBuilder)
25
+ ::Selenium::WebDriver::ActionBuilder.prepend(ActionPauser)
26
+ end
@@ -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
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Selenium
5
+ module IsDisplayed
6
+ def commands(command)
7
+ case command
8
+ when :is_element_displayed
9
+ [:get, 'session/:session_id/element/:id/displayed']
10
+ else
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -4,16 +4,41 @@ module Capybara
4
4
  module Selenium
5
5
  module ChromeLogs
6
6
  LOG_MSG = <<~MSG
7
- Chromedriver 75+ defaults to W3C mode. The W3C webdriver spec does not define methods for accessing \
8
- logs or log types. If you need to access the logs, in the short term, you can configure you driver to not use the W3C mode. \
9
- It is unknown how long non-W3C mode will be supported by chromedriver (it won't be supported by selenium-webdriver 4+) \
10
- so you may need to consider other solutions in the near future.
7
+ Chromedriver 75+ defaults to W3C mode. Please upgrade to chromedriver >= \
8
+ 75.0.3770.90 if you need to access logs while in W3C compliant mode.
11
9
  MSG
12
10
 
13
- def method_missing(meth, *) # rubocop:disable Style/MissingRespondToMissing
14
- raise NotImplementedError, LOG_MSG if %i[available_log_types log].include? meth
11
+ COMMANDS = {
12
+ get_available_log_types: [:get, 'session/:session_id/se/log/types'],
13
+ get_log: [:post, 'session/:session_id/se/log'],
14
+ get_log_legacy: [:post, 'session/:session_id/log']
15
+ }.freeze
15
16
 
16
- super
17
+ def commands(command)
18
+ COMMANDS[command] || super
19
+ end
20
+
21
+ def available_log_types
22
+ types = execute :get_available_log_types
23
+ Array(types).map(&:to_sym)
24
+ rescue ::Selenium::WebDriver::Error::UnknownCommandError
25
+ raise NotImplementedError, LOG_MSG
26
+ end
27
+
28
+ def log(type)
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
34
+
35
+ Array(data).map do |l|
36
+ ::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
37
+ rescue KeyError
38
+ next
39
+ end
40
+ rescue ::Selenium::WebDriver::Error::UnknownCommandError
41
+ raise NotImplementedError, LOG_MSG
17
42
  end
18
43
  end
19
44
  end
@@ -24,7 +24,9 @@ module Capybara
24
24
  host: Capybara.server_host,
25
25
  reportable_errors: Capybara.server_errors,
26
26
  extra_middleware: [])
27
- warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments' unless deprecated_options.empty?
27
+ unless deprecated_options.empty?
28
+ warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments'
29
+ end
28
30
  @app = app
29
31
  @extra_middleware = extra_middleware
30
32
  @server_thread = nil # suppress warnings
@@ -61,7 +63,7 @@ module Capybara
61
63
  def wait_for_pending_requests
62
64
  timer = Capybara::Helpers.timer(expire_in: 60)
63
65
  while pending_requests?
64
- raise 'Requests did not finish in 60 seconds' if timer.expired?
66
+ raise "Requests did not finish in 60 seconds: #{middleware.pending_requests}" if timer.expired?
65
67
 
66
68
  sleep 0.01
67
69
  end
@@ -86,6 +88,10 @@ module Capybara
86
88
  self
87
89
  end
88
90
 
91
+ def base_url
92
+ "http#{'s' if using_ssl?}://#{host}:#{port}"
93
+ end
94
+
89
95
  private
90
96
 
91
97
  def middleware
@@ -102,7 +108,17 @@ module Capybara
102
108
 
103
109
  def find_available_port(host)
104
110
  server = TCPServer.new(host, 0)
105
- server.addr[1]
111
+ port = server.addr[1]
112
+ server.close
113
+
114
+ # Workaround issue where some platforms (mac, ???) when passed a host
115
+ # of '0.0.0.0' will return a port that is only available on one of the
116
+ # ip addresses that resolves to, but the next binding to that port requires
117
+ # that port to be available on all ips
118
+ server = TCPServer.new(host, port)
119
+ port
120
+ rescue Errno::EADDRINUSE
121
+ retry
106
122
  ensure
107
123
  server&.close
108
124
  end
@@ -40,16 +40,21 @@ 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{(</body>)}, "#{disable_markup}\\1")
44
44
  end
45
45
 
46
46
  DISABLE_MARKUP_TEMPLATE = <<~HTML
47
- <script defer>(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);</script>
47
+ <script>
48
+ //<![CDATA[
49
+ (typeof jQuery !== 'undefined') && (jQuery.fx.off = true);
50
+ //]]>
51
+ </script>
48
52
  <style>
49
- %<selector>s {
53
+ %<selector>s, %<selector>s::before, %<selector>s::after {
50
54
  transition: none !important;
51
55
  animation-duration: 0s !important;
52
56
  animation-delay: 0s !important;
57
+ scroll-behavior: auto !important;
53
58
  }
54
59
  </style>
55
60
  HTML