capybara 3.22.0 → 3.23.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 (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