capybara 3.22.0 → 3.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +14 -0
  3. data/README.md +13 -3
  4. data/lib/capybara.rb +1 -1
  5. data/lib/capybara/driver/node.rb +1 -1
  6. data/lib/capybara/helpers.rb +1 -1
  7. data/lib/capybara/node/actions.rb +15 -13
  8. data/lib/capybara/node/element.rb +3 -2
  9. data/lib/capybara/queries/selector_query.rb +10 -8
  10. data/lib/capybara/selector.rb +11 -9
  11. data/lib/capybara/selenium/driver.rb +1 -0
  12. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +6 -0
  13. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +122 -0
  14. data/lib/capybara/selenium/extensions/html5_drag.rb +45 -36
  15. data/lib/capybara/selenium/node.rb +7 -1
  16. data/lib/capybara/selenium/nodes/chrome_node.rb +1 -7
  17. data/lib/capybara/selenium/nodes/edge_node.rb +86 -0
  18. data/lib/capybara/selenium/nodes/firefox_node.rb +0 -6
  19. data/lib/capybara/selenium/patches/atoms.rb +1 -1
  20. data/lib/capybara/selenium/patches/logs.rb +20 -0
  21. data/lib/capybara/server/checker.rb +1 -1
  22. data/lib/capybara/session.rb +1 -3
  23. data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
  24. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  25. data/lib/capybara/spec/session/node_spec.rb +22 -0
  26. data/lib/capybara/spec/session/window/window_spec.rb +3 -2
  27. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  28. data/lib/capybara/version.rb +1 -1
  29. data/lib/capybara/window.rb +1 -1
  30. data/spec/selenium_spec_chrome.rb +18 -2
  31. data/spec/selenium_spec_edge.rb +16 -6
  32. data/spec/shared_selenium_session.rb +4 -1
  33. data/spec/spec_helper.rb +19 -2
  34. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33bde7b48182b5c3dd5679c621882392d87bc8b9303cffabf963fe84186768b1
4
- data.tar.gz: 4d9ccd2f5c45a87586f87f29f9df888af7102bf6e86042993c36ed858fc07537
3
+ metadata.gz: 99599c60a1bafbd60b15e476dbaff54e5692358aed4e55908a40d537ebf00194
4
+ data.tar.gz: 26cb00377b6c80746e29838c8a22630a90786d73fa588e0ab9b3dd09fb7de9b9
5
5
  SHA512:
6
- metadata.gz: a128be647b867602954cc51cf603e020c3eceb938b5761766982a00e91fd0fccbc7aff02c49fc2c3064cdf489cc7a812c25818b5ca675ddf87c6a7a6822bd45a
7
- data.tar.gz: 40d9831cc35bf0d1af71722718cea81ac2a075b5bbc067a89edb78e56572d8e0ad56455abd47b2b5ba3e0edd5b76ed4eb77de63255773e70cfd261bfa064092f
6
+ metadata.gz: ea61766410acfe21525a56d25d44960a3c178b4d2c210bbc1f28bcc6c925f2bcbeeb1fd821a3618f94a775d7054291b3b831e411a75a0cb1ad6dd3ca517c0e91
7
+ data.tar.gz: de3a8a91967058710ebc0e30953cb22ea1fae30f5475ab32599a0c088ce032cba385721fe79a84cb5f81597bfc8b230afd5dabfb8e28391c1d7102453e251832
data/History.md CHANGED
@@ -1,3 +1,17 @@
1
+ # Version 3.23.0
2
+ Release date: 2019-06-10
3
+
4
+ ### Added
5
+
6
+ * Improved error message when using Chrome in W3C mode and attempting to access logs
7
+ * Support driver specific options for Element#drag_to
8
+ * Support setting `<input type="color">` elements with the selenium driver
9
+
10
+ ### Fixed
11
+
12
+ * Tightened conditions when in expression text option matching will be used
13
+ * Improved Selenium drivers HTML5 drag and drop emulation compatibility with SortableJS library (and others)
14
+
1
15
  # Version 3.22.0
2
16
  Release date: 2019-05-29
3
17
 
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8
8
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
9
9
 
10
- **Note** You are viewing the README for the 3.22.x version of Capybara.
10
+ **Note** You are viewing the README for the 3.23.x version of Capybara.
11
11
 
12
12
 
13
13
  Capybara helps you test web applications by simulating how a real user would
@@ -610,13 +610,23 @@ In drivers which support it, you can easily execute JavaScript:
610
610
  page.execute_script("$('body').empty()")
611
611
  ```
612
612
 
613
- For simple expressions, you can return the result of the script. Note
614
- that this may break with more complicated expressions:
613
+ For simple expressions, you can return the result of the script.
615
614
 
616
615
  ```ruby
617
616
  result = page.evaluate_script('4 + 4');
618
617
  ```
619
618
 
619
+ For more complicated scripts you'll need to write them as one expression.
620
+
621
+ ```ruby
622
+ result = page.evaluate_script(<<~JS, 3, element)
623
+ (function(n, el){
624
+ var val = parseInt(el.value, 10);
625
+ return n+val;
626
+ })(arguments[0], arguments[1])
627
+ JS
628
+ ```
629
+
620
630
  ### <a name="modals"></a>Modals
621
631
 
622
632
  In drivers which support it, you can accept, dismiss and respond to alerts, confirms and prompts.
@@ -95,7 +95,7 @@ module Capybara
95
95
  # - **server** (Symbol = `:default` (which uses puma)) - The name of the registered server to use when running the app under test.
96
96
  # - **server_errors** (Array\<Class> = `[Exception]`) - Error classes that should be raised in the tests if they are raised in the server
97
97
  # and {configure raise_server_errors} is `true`.
98
- # - **test_id** (`Symbol`, `String`, `nil` = `nil`) - Optional attribute to match locator against with built-in selectors along with id.
98
+ # - **test_id** (Symbol, String, `nil` = `nil`) - Optional attribute to match locator against with built-in selectors along with id.
99
99
  # - **threadsafe** (Boolean = `false`) - Whether sessions can be configured individually.
100
100
  #
101
101
  # #### DSL Options
@@ -65,7 +65,7 @@ module Capybara
65
65
  raise NotImplementedError
66
66
  end
67
67
 
68
- def drag_to(element)
68
+ def drag_to(element, **options)
69
69
  raise NotImplementedError
70
70
  end
71
71
 
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  # @api private
5
5
  module Helpers
6
- module_function # rubocop:disable Layout/IndentationWidth
6
+ module_function
7
7
 
8
8
  ##
9
9
  # @deprecated
@@ -61,7 +61,7 @@ module Capybara
61
61
  ##
62
62
  #
63
63
  # Locate a text field or text area and fill it in with the given text.
64
- # The field can be found via its name, id, {Capybara.configure test_id} attribute, or label text.
64
+ # The field can be found via its name, id, {Capybara.configure test_id} attribute, placeholder, or label text.
65
65
  # If no locator is provided this will operate on self or a descendant.
66
66
  #
67
67
  # # will fill in a descendant fillable field with name, id, or label text matching 'Name'
@@ -98,8 +98,8 @@ module Capybara
98
98
  ##
99
99
  #
100
100
  # Find a descendant radio button and mark it as checked. The radio button can be found
101
- # via name, id or label text. If no locator is provided this will match against self or
102
- # a descendant.
101
+ # via name, id, {Capybara.configure test_id} attribute or label text. If no locator is
102
+ # provided this will match against self or a descendant.
103
103
  #
104
104
  # # will choose a descendant radio button with a name, id, or label text matching 'Male'
105
105
  # page.choose('Male')
@@ -125,8 +125,8 @@ module Capybara
125
125
  ##
126
126
  #
127
127
  # Find a descendant check box and mark it as checked. The check box can be found
128
- # via name, id or label text. If no locator is provided this will match against
129
- # self or a descendant.
128
+ # via name, id, {Capybara.configure test_id} attribute, or label text. If no locator
129
+ # is provided this will match against self or a descendant.
130
130
  #
131
131
  # # will check a descendant checkbox with a name, id, or label text matching 'German'
132
132
  # page.check('German')
@@ -153,8 +153,8 @@ module Capybara
153
153
  ##
154
154
  #
155
155
  # Find a descendant check box and uncheck it. The check box can be found
156
- # via name, id or label text. If no locator is provided this will match against
157
- # self or a descendant.
156
+ # via name, id, {Capybara.configure test_id} attribute, or label text. If
157
+ # no locator is provided this will match against self or a descendant.
158
158
  #
159
159
  # # will uncheck a descendant checkbox with a name, id, or label text matching 'German'
160
160
  # page.uncheck('German')
@@ -185,7 +185,8 @@ module Capybara
185
185
  # Otherwise it finds an option inside current scope and selects it.
186
186
  # If the select box is a multiple select, {#select} can be called multiple times to select more than
187
187
  # one option.
188
- # The select box can be found via its name, id or label text. The option can be found by its text.
188
+ # The select box can be found via its name, id, {Capybara.configure test_id} attribute, or label text.
189
+ # The option can be found by its text.
189
190
  #
190
191
  # page.select 'March', from: 'Month'
191
192
  #
@@ -212,7 +213,8 @@ module Capybara
212
213
  #
213
214
  # Find a select box on the page and unselect a particular option from it. If the select
214
215
  # box is a multiple select, {#unselect} can be called multiple times to unselect more than
215
- # one option. The select box can be found via its name, id or label text.
216
+ # one option. The select box can be found via its name, id, {Capybara.configure test_id} attribute,
217
+ # or label text.
216
218
  #
217
219
  # page.unselect 'March', from: 'Month'
218
220
  #
@@ -234,8 +236,8 @@ module Capybara
234
236
  ##
235
237
  #
236
238
  # Find a descendant file field on the page and attach a file given its path. There are two ways to use
237
- # {#attach_file}, in the first method the file field can be found via its name, id or label text.
238
- # In the case of the file field being hidden for
239
+ # {#attach_file}, in the first method the file field can be found via its name, id,
240
+ # {Capybara.configure test_id} attribute, or label text. In the case of the file field being hidden for
239
241
  # styling reasons the `make_visible` option can be used to temporarily change the CSS of
240
242
  # the file field, attach the file, and then revert the CSS back to original. If no locator is
241
243
  # passed this will match self or a descendant.
@@ -305,12 +307,12 @@ module Capybara
305
307
  synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
306
308
  begin
307
309
  find(:select, from, options)
308
- rescue Capybara::ElementNotFound => select_error # _rubocop:disable Naming/RescuedExceptionsVariableName
310
+ rescue Capybara::ElementNotFound => select_error
309
311
  raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
310
312
 
311
313
  begin
312
314
  find(:datalist_input, from, options)
313
- rescue Capybara::ElementNotFound => dlinput_error # _rubocop:disable Naming/RescuedExceptionsVariableName
315
+ rescue Capybara::ElementNotFound => dlinput_error
314
316
  raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
315
317
  end
316
318
  end
@@ -396,10 +396,11 @@ module Capybara
396
396
  # source.drag_to(target)
397
397
  #
398
398
  # @param [Capybara::Node::Element] node The element to drag to
399
+ # @param [Hash] options Driver specific options for dragging. May not be supported by all drivers.
399
400
  #
400
401
  # @return [Capybara::Node::Element] The element
401
- def drag_to(node)
402
- synchronize { base.drag_to(node.base) }
402
+ def drag_to(node, **options)
403
+ synchronize { base.drag_to(node.base, **options) }
403
404
  self
404
405
  end
405
406
 
@@ -89,7 +89,7 @@ module Capybara
89
89
  matches_system_filters?(node) &&
90
90
  matches_node_filters?(node, node_filter_errors) &&
91
91
  matches_filter_block?(node)
92
- rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : []) # _rubocop:disable Naming/RescuedExceptionsVariableName
92
+ rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
93
93
  false
94
94
  end
95
95
 
@@ -154,14 +154,16 @@ module Capybara
154
154
  @selector.format
155
155
  end
156
156
 
157
+ def matching_text
158
+ options[:text] || options[:exact_text]
159
+ end
160
+
157
161
  def text_fragments
158
- text = (options[:text] || options[:exact_text])
159
- text.is_a?(String) ? text.split : []
162
+ (text = matching_text).is_a?(String) ? text.split : []
160
163
  end
161
164
 
162
165
  def xpath_text_conditions
163
- text = (options[:text] || options[:exact_text])
164
- case text
166
+ case (text = matching_text)
165
167
  when String
166
168
  text.split.map { |txt| XPath.contains(txt) }.reduce(&:&)
167
169
  when Regexp
@@ -175,9 +177,9 @@ module Capybara
175
177
 
176
178
  def try_text_match_in_expression?
177
179
  first_try? &&
178
- (options[:text] || options[:exact_text]) &&
179
- @resolved_node&.respond_to?(:session) &&
180
- @resolved_node.session.driver.wait?
180
+ matching_text &&
181
+ @resolved_node.is_a?(Capybara::Node::Base) &&
182
+ @resolved_node.session&.driver&.wait?
181
183
  end
182
184
 
183
185
  def first_try?
@@ -8,7 +8,7 @@ require 'capybara/selector/definition'
8
8
  # All Selectors below support the listed selector specific filters in addition to the following system-wide filters
9
9
  # * :id (String, Regexp, XPath::Expression) - Matches the id attribute
10
10
  # * :class (String, Array<String>, Regexp, XPath::Expression) - Matches the class(es) provided
11
- # * :style (String, Regexp, Hash<String,String>) - Match on elements style
11
+ # * :style (String, Regexp, Hash<String, String>) - Match on elements style
12
12
  #
13
13
  # ### Built-in Selectors
14
14
  #
@@ -22,7 +22,8 @@ require 'capybara/selector/definition'
22
22
  # * Locator: (String, Regexp, XPath::Expression) The id of the element to match
23
23
  #
24
24
  # * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
25
- # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
25
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, placeholder, or
26
+ # associated label text
26
27
  # * Filters:
27
28
  # * :name (String) - Matches the name attribute
28
29
  # * :placeholder (String) - Matches the placeholder attribute
@@ -41,8 +42,8 @@ require 'capybara/selector/definition'
41
42
  # * :disabled (Boolean) - Match disabled fieldset?
42
43
  #
43
44
  # * **:link** - Find links (`<a>` elements with an href attribute)
44
- # * Locator: Matches the id, {Capybara.configure test_id}, or title attributes, or the string content of the link, or the alt attribute of a contained img element.
45
- # By default this selector requires a link to have an href attribute.
45
+ # * Locator: Matches the id, {Capybara.configure test_id}, or title attributes, or the string content of the link,
46
+ # or the alt attribute of a contained img element. By default this selector requires a link to have an href attribute.
46
47
  # * Filters:
47
48
  # * :title (String) - Matches the title attribute
48
49
  # * :alt (String) - Matches the alt attribute of a contained img element
@@ -63,7 +64,7 @@ require 'capybara/selector/definition'
63
64
  # * :disabled (Boolean, :all) - Match disabled buttons? (Default: false)
64
65
  #
65
66
  # * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
66
- # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
67
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, placeholder, or associated label text
67
68
  # * Filters:
68
69
  # * :name (String) - Matches the name attribute
69
70
  # * :placeholder (String) - Matches the placeholder attribute
@@ -111,7 +112,8 @@ require 'capybara/selector/definition'
111
112
  # * :selected (Boolean) - Match selected option
112
113
  #
113
114
  # * **:datalist_input** - Find input field with datalist completion
114
- # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name, or placeholder
115
+ # * Locator: Matches against the id, {Capybara.configure test_id} attribute, name,
116
+ # placeholder, or associated label text
115
117
  # * Filters:
116
118
  # * :name (String) - Matches the name attribute
117
119
  # * :placeholder (String) - Matches the placeholder attribute
@@ -146,17 +148,17 @@ require 'capybara/selector/definition'
146
148
  # * :cols (Array<Array<String>>) - Match all `<td>`s - visibility of `<td>` elements is not considered
147
149
  #
148
150
  # * **:table_row** - Find table row
149
- # * Locator: Array<String>, Hash<String,String> table row `<td>` contents - visibility of `<td>` elements is not considered
151
+ # * Locator: Array<String>, Hash<String, String> table row `<td>` contents - visibility of `<td>` elements is not considered
150
152
  #
151
153
  # * **:frame** - Find frame/iframe elements
152
- # * Locator: Match id or name
154
+ # * Locator: Match id, {Capybara.configure test_id} attribute, or name
153
155
  # * Filters:
154
156
  # * :name (String) - Match name attribute
155
157
  #
156
158
  # * **:element**
157
159
  # * Locator: Type of element ('div', 'a', etc) - if not specified defaults to '*'
158
160
  # * Filters:
159
- # * :&lt;any> (String, Regexp) - Match on any specified element attribute
161
+ # * :\<any> (String, Regexp) - Match on any specified element attribute
160
162
  #
161
163
  class Capybara::Selector; end
162
164
 
@@ -475,3 +475,4 @@ require 'capybara/selenium/driver_specializations/chrome_driver'
475
475
  require 'capybara/selenium/driver_specializations/firefox_driver'
476
476
  require 'capybara/selenium/driver_specializations/internet_explorer_driver'
477
477
  require 'capybara/selenium/driver_specializations/safari_driver'
478
+ require 'capybara/selenium/driver_specializations/edge_driver'
@@ -1,8 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'capybara/selenium/nodes/chrome_node'
4
+ require 'capybara/selenium/patches/logs'
4
5
 
5
6
  module Capybara::Selenium::Driver::ChromeDriver
7
+ def self.extended(base)
8
+ bridge = base.send(:bridge)
9
+ bridge.extend Capybara::Selenium::ChromeLogs unless bridge.respond_to?(:available_log_types)
10
+ end
11
+
6
12
  def fullscreen_window(handle)
7
13
  within_given_window(handle) do
8
14
  begin
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/nodes/edge_node'
4
+
5
+ module Capybara::Selenium::Driver::EdgeDriver
6
+ def fullscreen_window(handle)
7
+ return super if edgedriver_version < 75
8
+
9
+ within_given_window(handle) do
10
+ begin
11
+ super
12
+ rescue NoMethodError => e
13
+ raise unless e.message.match?(/full_screen_window/)
14
+
15
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
16
+ result['value']
17
+ end
18
+ end
19
+ end
20
+
21
+ def resize_window_to(handle, width, height)
22
+ super
23
+ rescue Selenium::WebDriver::Error::UnknownError => e
24
+ raise unless e.message.match?(/failed to change window state/)
25
+
26
+ # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen
27
+ # and raises unnecessary error. Wait a bit and try again.
28
+ sleep 0.25
29
+ super
30
+ end
31
+
32
+ def reset!
33
+ return super if edgedriver_version < 75
34
+ # Use instance variable directly so we avoid starting the browser just to reset the session
35
+ return unless @browser
36
+
37
+ switch_to_window(window_handles.first)
38
+ window_handles.slice(1..-1).each { |win| close_window(win) }
39
+
40
+ timer = Capybara::Helpers.timer(expire_in: 10)
41
+ begin
42
+ @browser.navigate.to('about:blank')
43
+ clear_storage unless uniform_storage_clear?
44
+ wait_for_empty_page(timer)
45
+ rescue *unhandled_alert_errors
46
+ accept_unhandled_reset_alert
47
+ retry
48
+ end
49
+
50
+ execute_cdp('Storage.clearDataForOrigin', origin: '*', storageTypes: storage_types_to_clear)
51
+ end
52
+
53
+ def download_path=(path)
54
+ if @browser.respond_to?(:download_path=)
55
+ @browser.download_path = path
56
+ else
57
+ # Not yet implemented in seleniun-webdriver for edge so do it ourselves
58
+ execute_cdp('Page.setDownloadBehavior', behavior: 'allow', downloadPath: path)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def storage_types_to_clear
65
+ types = ['cookies']
66
+ types << 'local_storage' if clear_all_storage?
67
+ types.join(',')
68
+ end
69
+
70
+ def clear_all_storage?
71
+ options.values_at(:clear_session_storage, :clear_local_storage).none? { |s| s == false }
72
+ end
73
+
74
+ def uniform_storage_clear?
75
+ clear = options.values_at(:clear_session_storage, :clear_local_storage)
76
+ clear.all? { |s| s == false } || clear.none? { |s| s == false }
77
+ end
78
+
79
+ def clear_storage
80
+ # Chrome errors if attempt to clear storage on about:blank
81
+ # In W3C mode it crashes chromedriver
82
+ url = current_url
83
+ super unless url.nil? || url.start_with?('about:')
84
+ end
85
+
86
+ def delete_all_cookies
87
+ return super if edgedriver_version < 75
88
+
89
+ execute_cdp('Network.clearBrowserCookies')
90
+ rescue *cdp_unsupported_errors
91
+ # If the CDP clear isn't supported do original limited clear
92
+ super
93
+ end
94
+
95
+ def cdp_unsupported_errors
96
+ @cdp_unsupported_errors ||= [Selenium::WebDriver::Error::WebDriverError]
97
+ end
98
+
99
+ def execute_cdp(cmd, params = {})
100
+ args = { cmd: cmd, params: params }
101
+ result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
102
+ result['value']
103
+ end
104
+
105
+ def build_node(native_node, initial_cache = {})
106
+ ::Capybara::Selenium::EdgeNode.new(self, native_node, initial_cache)
107
+ end
108
+
109
+ def bridge
110
+ browser.send(:bridge)
111
+ end
112
+
113
+ def edgedriver_version
114
+ @edgedriver_version ||= begin
115
+ caps = browser.capabilities
116
+ caps['chrome']&.fetch('chromedriverVersion', nil).to_f
117
+ end
118
+ end
119
+ end
120
+
121
+ Capybara::Selenium::Driver.register_specialization :edge, Capybara::Selenium::Driver::EdgeDriver
122
+ Capybara::Selenium::Driver.register_specialization :edge_chrome, Capybara::Selenium::Driver::EdgeDriver
@@ -2,25 +2,20 @@
2
2
 
3
3
  class Capybara::Selenium::Node
4
4
  module Html5Drag
5
- # Implement methods to emulate HTML5 drag and drop
5
+ # Implement methods to emulate HTML5 drag and drop
6
6
 
7
- private # rubocop:disable Layout/IndentationWidth
8
-
9
- def html5_drag_to(element)
7
+ def drag_to(element, delay: 0.05)
10
8
  driver.execute_script MOUSEDOWN_TRACKER
11
9
  scroll_if_needed { browser_action.click_and_hold(native).perform }
12
- if driver.evaluate_script('window.capybara_mousedown_prevented')
10
+ if driver.evaluate_script('window.capybara_mousedown_prevented || !arguments[0].draggable', self)
13
11
  element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
14
12
  else
15
- driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element
13
+ driver.evaluate_async_script HTML5_DRAG_DROP_SCRIPT, self, element, delay * 1000
16
14
  browser_action.release.perform
17
15
  end
18
16
  end
19
17
 
20
- def html5_draggable?
21
- # Workaround https://github.com/SeleniumHQ/selenium/issues/6396
22
- native.property('draggable')
23
- end
18
+ private
24
19
 
25
20
  def html5_drop(*args)
26
21
  if args[0].is_a? String
@@ -92,9 +87,6 @@ class Capybara::Selenium::Node
92
87
  JS
93
88
 
94
89
  HTML5_DRAG_DROP_SCRIPT = <<~JS
95
- var source = arguments[0];
96
- var target = arguments[1];
97
-
98
90
  function rectCenter(rect){
99
91
  return new DOMPoint(
100
92
  (rect.left + rect.right)/2,
@@ -133,6 +125,44 @@ class Capybara::Selenium::Node
133
125
  return new DOMPoint(pt.x,pt.y);
134
126
  }
135
127
 
128
+ function dragEnterTarget() {
129
+ target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
130
+ var targetRect = target.getBoundingClientRect();
131
+ var sourceCenter = rectCenter(source.getBoundingClientRect());
132
+
133
+ // fire 2 dragover events to simulate dragging with a direction
134
+ var entryPoint = pointOnRect(sourceCenter, targetRect)
135
+ var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
136
+ var dragOverEvent = new DragEvent('dragover', dragOverOpts);
137
+ target.dispatchEvent(dragOverEvent);
138
+ window.setTimeout(dragOnTarget, step_delay);
139
+ }
140
+
141
+ function dragOnTarget() {
142
+ var targetCenter = rectCenter(target.getBoundingClientRect());
143
+ var dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts);
144
+ var dragOverEvent = new DragEvent('dragover', dragOverOpts);
145
+ target.dispatchEvent(dragOverEvent);
146
+ window.setTimeout(dragLeave, step_delay, dragOverEvent.defaultPrevented);
147
+ }
148
+
149
+ function dragLeave(drop) {
150
+ var dragLeaveEvent = new DragEvent('dragleave', opts);
151
+ target.dispatchEvent(dragLeaveEvent);
152
+ if (drop) {
153
+ var dropEvent = new DragEvent('drop', opts);
154
+ target.dispatchEvent(dropEvent);
155
+ }
156
+ var dragEndEvent = new DragEvent('dragend', opts);
157
+ source.dispatchEvent(dragEndEvent);
158
+ callback.call(true);
159
+ }
160
+
161
+ var source = arguments[0],
162
+ target = arguments[1],
163
+ step_delay = arguments[2],
164
+ callback = arguments[3];
165
+
136
166
  var dt = new DataTransfer();
137
167
  var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
138
168
 
@@ -147,29 +177,8 @@ class Capybara::Selenium::Node
147
177
 
148
178
  var dragEvent = new DragEvent('dragstart', opts);
149
179
  source.dispatchEvent(dragEvent);
150
- target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
151
- var targetRect = target.getBoundingClientRect();
152
- var sourceCenter = rectCenter(source.getBoundingClientRect());
153
-
154
- // fire 2 dragover events to simulate dragging with a direction
155
- var entryPoint = pointOnRect(sourceCenter, targetRect)
156
- var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
157
- var dragOverEvent = new DragEvent('dragover', dragOverOpts);
158
- target.dispatchEvent(dragOverEvent);
159
-
160
- var targetCenter = rectCenter(targetRect);
161
- dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts);
162
- dragOverEvent = new DragEvent('dragover', dragOverOpts);
163
- target.dispatchEvent(dragOverEvent);
164
-
165
- var dragLeaveEvent = new DragEvent('dragleave', opts);
166
- target.dispatchEvent(dragLeaveEvent);
167
- if (dragOverEvent.defaultPrevented) {
168
- var dropEvent = new DragEvent('drop', opts);
169
- target.dispatchEvent(dropEvent);
170
- }
171
- var dragEndEvent = new DragEvent('dragend', opts);
172
- source.dispatchEvent(dragEndEvent);
180
+
181
+ window.setTimeout(dragEnterTarget, step_delay);
173
182
  JS
174
183
  end
175
184
  end
@@ -74,6 +74,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
74
74
  set_time(value)
75
75
  when 'datetime-local'
76
76
  set_datetime_local(value)
77
+ when 'color'
78
+ set_color(value)
77
79
  else
78
80
  set_text(value, options)
79
81
  end
@@ -130,7 +132,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
130
132
  scroll_if_needed { browser_action.move_to(native).perform }
131
133
  end
132
134
 
133
- def drag_to(element)
135
+ def drag_to(element, **)
134
136
  # Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary
135
137
  # which means Seleniums `drag_and_drop` is now broken - do it manually
136
138
  scroll_if_needed { browser_action.click_and_hold(native).perform }
@@ -281,6 +283,10 @@ private
281
283
  update_value_js(value.to_datetime_str)
282
284
  end
283
285
 
286
+ def set_color(value) # rubocop:disable Naming/AccessorMethodName
287
+ update_value_js(value)
288
+ end
289
+
284
290
  def update_value_js(value)
285
291
  driver.execute_script(<<-JS, self, value)
286
292
  if (arguments[0].readOnly) { return };
@@ -21,19 +21,13 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
21
21
  }
22
22
  JS
23
23
  end
24
- super(value)
24
+ super
25
25
  rescue *file_errors => e
26
26
  raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
27
27
 
28
28
  raise
29
29
  end
30
30
 
31
- def drag_to(element)
32
- return super unless html5_draggable?
33
-
34
- html5_drag_to(element)
35
- end
36
-
37
31
  def drop(*args)
38
32
  html5_drop(*args)
39
33
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selenium/extensions/html5_drag'
4
+
5
+ class Capybara::Selenium::EdgeNode < Capybara::Selenium::Node
6
+ include Html5Drag
7
+
8
+ def set_text(value, clear: nil, **_unused)
9
+ return super unless chrome_edge?
10
+
11
+ super.tap do
12
+ # React doesn't see the chromedriver element clear
13
+ send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
14
+ end
15
+ end
16
+
17
+ def set_file(value) # rubocop:disable Naming/AccessorMethodName
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
+ if chrome_edge?
20
+ driver.execute_script(<<~JS, self)
21
+ if (arguments[0].multiple && (arguments[0].files.length > 0)){
22
+ arguments[0].value = null;
23
+ }
24
+ JS
25
+ end
26
+ super
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)
29
+
30
+ raise
31
+ end
32
+
33
+ def drop(*args)
34
+ return super unless chrome_edge?
35
+
36
+ html5_drop(*args)
37
+ end
38
+
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
47
+
48
+ def disabled?
49
+ return super unless chrome_edge?
50
+
51
+ driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
52
+ end
53
+
54
+ def select_option
55
+ return super unless chrome_edge?
56
+
57
+ # To optimize to only one check and then click
58
+ selected_or_disabled = driver.evaluate_script(<<~JS, self)
59
+ arguments[0].matches(':disabled, select:disabled *, :checked')
60
+ JS
61
+ click unless selected_or_disabled
62
+ end
63
+
64
+ private
65
+
66
+ def file_errors
67
+ @file_errors = ::Selenium::WebDriver.logger.suppress_deprecations do
68
+ [::Selenium::WebDriver::Error::ExpectedError]
69
+ end
70
+ end
71
+
72
+ def bridge
73
+ driver.browser.send(:bridge)
74
+ end
75
+
76
+ def browser_version
77
+ @browser_version ||= begin
78
+ caps = driver.browser.capabilities
79
+ (caps[:browser_version] || caps[:version]).to_f
80
+ end
81
+ end
82
+
83
+ def chrome_edge?
84
+ browser_version >= 75
85
+ end
86
+ end
@@ -46,12 +46,6 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
46
46
  _send_keys(args).perform
47
47
  end
48
48
 
49
- def drag_to(element)
50
- return super unless (browser_version >= 62.0) && html5_draggable?
51
-
52
- html5_drag_to(element)
53
- end
54
-
55
49
  def drop(*args)
56
50
  html5_drop(*args)
57
51
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CapybaraAtoms
4
- private # rubocop:disable Layout/IndentationWidth
4
+ private
5
5
 
6
6
  def read_atom(function)
7
7
  @atoms ||= Hash.new do |hash, key|
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Selenium
5
+ module ChromeLogs
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.
11
+ MSG
12
+
13
+ def method_missing(meth, *) # rubocop:disable Style/MissingRespondToMissing
14
+ raise NotImplementedError, LOG_MSG if %i[available_log_types log].include? meth
15
+
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
@@ -12,7 +12,7 @@ module Capybara
12
12
 
13
13
  def request(&block)
14
14
  ssl? ? https_request(&block) : http_request(&block)
15
- rescue *TRY_HTTPS_ERRORS # _rubocop:disable Naming/RescuedExceptionsVariableName
15
+ rescue *TRY_HTTPS_ERRORS
16
16
  res = https_request(&block)
17
17
  @ssl = true
18
18
  res
@@ -736,9 +736,7 @@ module Capybara
736
736
  # @param [Hash] options a customizable set of options
737
737
  #
738
738
  def save_and_open_screenshot(path = nil, **options)
739
- # rubocop:disable Lint/Debugger
740
- save_screenshot(path, options).tap { |s_path| open_file(s_path) }
741
- # rubocop:enable Lint/Debugger
739
+ save_screenshot(path, options).tap { |s_path| open_file(s_path) } # rubocop:disable Lint/Debugger
742
740
  end
743
741
 
744
742
  def document
@@ -34,4 +34,16 @@ Capybara::SpecHelper.spec '#evaluate_script', requires: [:js] do
34
34
  expect(el).to be_instance_of(Capybara::Node::Element)
35
35
  expect(el).to eq(@session.find(:css, '#change'))
36
36
  end
37
+
38
+ it 'should support multi statement via IIFE' do
39
+ @session.visit('/with_js')
40
+ @session.find(:css, '#change')
41
+ el = @session.evaluate_script(<<~JS)
42
+ (function(){
43
+ var el = document.getElementById('change');
44
+ return el;
45
+ })()
46
+ JS
47
+ expect(el).to eq(@session.find(:css, '#change'))
48
+ end
37
49
  end
@@ -89,6 +89,12 @@ Capybara::SpecHelper.spec '#fill_in' do
89
89
  expect(extract_results(@session)['description']).to eq("\r\nSome text\r\n")
90
90
  end
91
91
 
92
+ it 'should fill in a color field' do
93
+ @session.fill_in('Html5 Color', with: '#112233')
94
+ @session.click_button('html5_submit')
95
+ expect(extract_results(@session)['html5_color']).to eq('#112233')
96
+ end
97
+
92
98
  it 'should fill in a field with a custom type' do
93
99
  @session.fill_in('Schmooo', with: 'Schmooo is the game')
94
100
  @session.click_button('awesome')
@@ -461,6 +461,16 @@ Capybara::SpecHelper.spec 'node' do
461
461
  link.drag_to target
462
462
  expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
463
463
  end
464
+
465
+ it 'should work with SortableJS' do
466
+ @session.visit('/with_sortable_js')
467
+ @session.within(:css, '#sortable') do
468
+ src = @session.find('div', text: 'Item 1')
469
+ target = @session.find('div', text: 'Item 3')
470
+ src.drag_to target
471
+ expect(@session).to have_content(/Item 3.*Item 1/, normalize_ws: true)
472
+ end
473
+ end
464
474
  end
465
475
  end
466
476
 
@@ -774,6 +784,18 @@ Capybara::SpecHelper.spec 'node' do
774
784
  expect(el).to be_instance_of(Capybara::Node::Element)
775
785
  expect(el).to eq(change)
776
786
  end
787
+
788
+ it 'should support multiple statements via IIFE' do
789
+ @session.visit('/with_js')
790
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
791
+ res = change.evaluate_script(<<~JS, 3)
792
+ (function(n){
793
+ var el = this;
794
+ return [el, n];
795
+ }).apply(this, arguments)
796
+ JS
797
+ expect(res).to eq [change, 3]
798
+ end
777
799
  end
778
800
 
779
801
  describe '#evaluate_async_script', requires: %i[js es_args] do
@@ -130,11 +130,12 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
130
130
  other_window = @session.window_opened_by do
131
131
  @session.find(:css, '#openWindow').click
132
132
  end
133
- other_window.resize_to(600, 300)
133
+
134
+ other_window.resize_to(600, 400)
134
135
  expect(@session.current_window).to eq(orig_window)
135
136
 
136
137
  @session.within_window(other_window) do
137
- expect(@session.current_window.size).to eq([600, 300])
138
+ expect(@session.current_window.size).to eq([600, 400])
138
139
  end
139
140
  end
140
141
  end
@@ -0,0 +1,21 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
2
+ <head>
3
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
4
+ <title>with_sortable_js</title>
5
+ <script src="https://sortablejs.github.io/Sortable/Sortable.js" type="text/javascript"></script>
6
+ </head>
7
+
8
+ <body id="with_sortable_js">
9
+ <div id="sortable">
10
+ <div class="item1">Item 1</div>
11
+ <div class="item2">Item 2</div>
12
+ <div class="item3">Item 3</div>
13
+ <div class="item4">Item 4</div>
14
+ <div class="item5">Item 5</div>
15
+ </div>
16
+ <script>
17
+ new Sortable(document.getElementById("sortable"));
18
+ </script>
19
+ </body>
20
+ </html>
21
+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capybara
4
- VERSION = '3.22.0'
4
+ VERSION = '3.23.0'
5
5
  end
@@ -49,7 +49,7 @@ module Capybara
49
49
  # @return [Boolean] whether this window is the window in which commands are being executed
50
50
  def current?
51
51
  @driver.current_window_handle == @handle
52
- rescue @driver.no_such_window_error # _rubocop:disable Naming/RescuedExceptionsVariableName
52
+ rescue @driver.no_such_window_error
53
53
  false
54
54
  end
55
55
 
@@ -8,11 +8,11 @@ require 'rspec/shared_spec_matchers'
8
8
 
9
9
  CHROME_DRIVER = :selenium_chrome
10
10
 
11
- Selenium::WebDriver::Chrome.path = '/usr/bin/google-chrome-beta' if ENV['CI'] && ENV['W3C']
11
+ Selenium::WebDriver::Chrome.path = '/usr/bin/google-chrome-beta' if ENV['CI'] && ENV['CHROME_BETA']
12
12
 
13
13
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
14
14
  browser_options.headless! if ENV['HEADLESS']
15
- browser_options.add_option(:w3c, !!ENV['W3C'])
15
+ browser_options.add_option(:w3c, ENV['W3C'] != 'false')
16
16
 
17
17
  Capybara.register_driver :selenium_chrome do |app|
18
18
  Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options, timeout: 30).tap do |driver|
@@ -121,4 +121,20 @@ RSpec.describe 'Capybara::Session with chrome' do
121
121
  expect(session).to have_current_path('/form')
122
122
  end
123
123
  end
124
+
125
+ describe 'log access' do
126
+ before { skip 'Only makes sense in W3C mode' if ENV['W3C'] == 'false' }
127
+
128
+ it 'errors when getting log types' do
129
+ expect do
130
+ session.driver.browser.manage.logs.available_types
131
+ end.to raise_error(NotImplementedError, /Chromedriver 75\+ defaults to W3C mode/)
132
+ end
133
+
134
+ it 'errors when getting logs' do
135
+ expect do
136
+ session.driver.browser.manage.logs.get(:browser)
137
+ end.to raise_error(NotImplementedError, /Chromedriver 75\+ defaults to W3C mode/)
138
+ end
139
+ end
124
140
  end
@@ -6,24 +6,34 @@ require 'shared_selenium_session'
6
6
  require 'shared_selenium_node'
7
7
  require 'rspec/shared_spec_matchers'
8
8
 
9
+ Selenium::WebDriver::Edge::Service.driver_path = '/usr/local/bin/msedgedriver'
10
+ Selenium::WebDriver::EdgeChrome.path = '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary'
11
+
9
12
  Capybara.register_driver :selenium_edge do |app|
10
13
  # ::Selenium::WebDriver.logger.level = "debug"
11
- Capybara::Selenium::Driver.new(app, browser: :edge)
14
+ Capybara::Selenium::Driver.new(app, browser: :edge_chrome).tap do |driver|
15
+ driver.browser
16
+ driver.download_path = Capybara.save_path
17
+ end
12
18
  end
13
19
 
14
20
  module TestSessions
15
21
  SeleniumEdge = Capybara::Session.new(:selenium_edge, TestApp)
16
22
  end
17
23
 
18
- skipped_tests = %i[response_headers status_code trigger modals]
24
+ skipped_tests = %i[response_headers status_code trigger]
19
25
 
20
26
  Capybara::SpecHelper.log_selenium_driver_version(Selenium::WebDriver::Edge) if ENV['CI']
21
27
 
22
28
  Capybara::SpecHelper.run_specs TestSessions::SeleniumEdge, 'selenium', capybara_skip: skipped_tests do |example|
23
- case example.metadata[:description]
24
- when /#refresh it reposts$/
25
- skip 'Edge insists on prompting without providing a way to suppress'
26
- end
29
+ # case example.metadata[:description]
30
+ # when /#refresh it reposts$/
31
+ # skip 'Edge insists on prompting without providing a way to suppress'
32
+ # when /should be able to open non-http url/
33
+ # skip 'Crashes'
34
+ # when /when Capybara.always_include_port is true/
35
+ # skip 'Crashes'
36
+ # end
27
37
  end
28
38
 
29
39
  RSpec.describe 'Capybara::Session with Edge', capybara_skip: skipped_tests do
@@ -41,6 +41,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
41
41
 
42
42
  it 'should have return code 1 when running selenium_driver_rspec_failure.rb' do
43
43
  skip 'only setup for local non-headless' if headless_or_remote?
44
+ skip 'Not setup for edge' if edge?(session)
44
45
 
45
46
  system(env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL)
46
47
  expect($CHILD_STATUS.exitstatus).to eq(1)
@@ -48,6 +49,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
48
49
 
49
50
  it 'should have return code 0 when running selenium_driver_rspec_success.rb' do
50
51
  skip 'only setup for local non-headless' if headless_or_remote?
52
+ skip 'Not setup for edge' if edge?(session)
51
53
 
52
54
  system(env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL)
53
55
  expect($CHILD_STATUS.exitstatus).to eq(0)
@@ -309,8 +311,9 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
309
311
  pending "Selenium remote doesn't support transferring a directory" if remote?(session)
310
312
  pending "Headless Chrome doesn't support directory upload - https://bugs.chromium.org/p/chromedriver/issues/detail?id=2521&q=directory%20upload&colspec=ID%20Status%20Pri%20Owner%20Summary" if chrome?(session) && ENV['HEADLESS']
311
313
  pending "IE doesn't support uploading a directory" if ie?(session)
312
- pending 'Chrome/chromedriver 73 breaks this' if chrome?(session) && !chrome_lt?(73, session)
314
+ pending 'Chrome/chromedriver 73 breaks this' if chrome?(session) && chrome_gte?(73, session) && chrome_lt?(75, session)
313
315
  pending "Safari doesn't support uploading a directory" if safari?(session)
316
+ # pending "Edge/msedgedriver doesn't support directory upload" if edge?(session) && edge_gte?(75, session)
314
317
 
315
318
  session.visit('/form')
316
319
  test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__))
@@ -32,18 +32,35 @@ module Capybara
32
32
  browser_name(session) == :chrome
33
33
  end
34
34
 
35
+ def chrome_version(session)
36
+ (session.driver.browser.capabilities[:browser_version] ||
37
+ session.driver.browser.capabilities[:version]).to_f
38
+ end
39
+
35
40
  def chrome_lt?(version, session)
36
- chrome?(session) && (session.driver.browser.capabilities[:version].to_f < version)
41
+ chrome?(session) && (chrome_version(session) < version)
37
42
  end
38
43
 
39
44
  def chrome_gte?(version, session)
40
- chrome?(session) && (session.driver.browser.capabilities[:version].to_f >= version)
45
+ chrome?(session) && (chrome_version(session) >= version)
41
46
  end
42
47
 
43
48
  def edge?(session)
49
+ browser_name(session).match?(/^edge/)
50
+ end
51
+
52
+ def legacy_edge?(session)
44
53
  browser_name(session) == :edge
45
54
  end
46
55
 
56
+ def edge_lt?(version, session)
57
+ edge?(session) && (chrome_version(session) < version)
58
+ end
59
+
60
+ def edge_gte?(version, session)
61
+ edge?(session) && (chrome_version(session) >= version)
62
+ end
63
+
47
64
  def ie?(session)
48
65
  %i[internet_explorer ie].include?(browser_name(session))
49
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.22.0
4
+ version: 3.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Walpole
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
12
12
  - gem-public_cert.pem
13
- date: 2019-05-29 00:00:00.000000000 Z
13
+ date: 2019-06-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: addressable
@@ -510,6 +510,7 @@ files:
510
510
  - lib/capybara/selenium/atoms/src/isDisplayed.js
511
511
  - lib/capybara/selenium/driver.rb
512
512
  - lib/capybara/selenium/driver_specializations/chrome_driver.rb
513
+ - lib/capybara/selenium/driver_specializations/edge_driver.rb
513
514
  - lib/capybara/selenium/driver_specializations/firefox_driver.rb
514
515
  - lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb
515
516
  - lib/capybara/selenium/driver_specializations/safari_driver.rb
@@ -519,10 +520,12 @@ files:
519
520
  - lib/capybara/selenium/logger_suppressor.rb
520
521
  - lib/capybara/selenium/node.rb
521
522
  - lib/capybara/selenium/nodes/chrome_node.rb
523
+ - lib/capybara/selenium/nodes/edge_node.rb
522
524
  - lib/capybara/selenium/nodes/firefox_node.rb
523
525
  - lib/capybara/selenium/nodes/ie_node.rb
524
526
  - lib/capybara/selenium/nodes/safari_node.rb
525
527
  - lib/capybara/selenium/patches/atoms.rb
528
+ - lib/capybara/selenium/patches/logs.rb
526
529
  - lib/capybara/selenium/patches/pause_duration_fix.rb
527
530
  - lib/capybara/selenium/patches/persistent_client.rb
528
531
  - lib/capybara/server.rb
@@ -665,6 +668,7 @@ files:
665
668
  - lib/capybara/spec/views/with_scope_other.erb
666
669
  - lib/capybara/spec/views/with_simple_html.erb
667
670
  - lib/capybara/spec/views/with_slow_unload.erb
671
+ - lib/capybara/spec/views/with_sortable_js.erb
668
672
  - lib/capybara/spec/views/with_title.erb
669
673
  - lib/capybara/spec/views/with_unload_alert.erb
670
674
  - lib/capybara/spec/views/with_windows.erb