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.
- checksums.yaml +4 -4
- data/History.md +14 -0
- data/README.md +13 -3
- data/lib/capybara.rb +1 -1
- data/lib/capybara/driver/node.rb +1 -1
- data/lib/capybara/helpers.rb +1 -1
- data/lib/capybara/node/actions.rb +15 -13
- data/lib/capybara/node/element.rb +3 -2
- data/lib/capybara/queries/selector_query.rb +10 -8
- data/lib/capybara/selector.rb +11 -9
- data/lib/capybara/selenium/driver.rb +1 -0
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +6 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +122 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +45 -36
- data/lib/capybara/selenium/node.rb +7 -1
- data/lib/capybara/selenium/nodes/chrome_node.rb +1 -7
- data/lib/capybara/selenium/nodes/edge_node.rb +86 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +0 -6
- data/lib/capybara/selenium/patches/atoms.rb +1 -1
- data/lib/capybara/selenium/patches/logs.rb +20 -0
- data/lib/capybara/server/checker.rb +1 -1
- data/lib/capybara/session.rb +1 -3
- data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/node_spec.rb +22 -0
- data/lib/capybara/spec/session/window/window_spec.rb +3 -2
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/selenium_spec_chrome.rb +18 -2
- data/spec/selenium_spec_edge.rb +16 -6
- data/spec/shared_selenium_session.rb +4 -1
- data/spec/spec_helper.rb +19 -2
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99599c60a1bafbd60b15e476dbaff54e5692358aed4e55908a40d537ebf00194
|
4
|
+
data.tar.gz: 26cb00377b6c80746e29838c8a22630a90786d73fa588e0ab9b3dd09fb7de9b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
[](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
8
8
|
[](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.
|
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.
|
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.
|
data/lib/capybara.rb
CHANGED
@@ -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** (
|
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
|
data/lib/capybara/driver/node.rb
CHANGED
data/lib/capybara/helpers.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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 : [])
|
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 = (
|
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
|
-
|
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
|
-
|
179
|
-
@resolved_node
|
180
|
-
@resolved_node.session
|
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?
|
data/lib/capybara/selector.rb
CHANGED
@@ -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
|
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,
|
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
|
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,
|
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
|
-
# *
|
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
|
-
|
5
|
+
# Implement methods to emulate HTML5 drag and drop
|
6
6
|
|
7
|
-
|
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.
|
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
|
-
|
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
|
-
|
151
|
-
|
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
|
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
|
@@ -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
|
15
|
+
rescue *TRY_HTTPS_ERRORS
|
16
16
|
res = https_request(&block)
|
17
17
|
@ssl = true
|
18
18
|
res
|
data/lib/capybara/session.rb
CHANGED
@@ -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
|
-
|
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,
|
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
|
+
|
data/lib/capybara/version.rb
CHANGED
data/lib/capybara/window.rb
CHANGED
@@ -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
|
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['
|
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,
|
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
|
data/spec/selenium_spec_edge.rb
CHANGED
@@ -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: :
|
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
|
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
|
-
|
26
|
-
|
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) &&
|
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__))
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
41
|
+
chrome?(session) && (chrome_version(session) < version)
|
37
42
|
end
|
38
43
|
|
39
44
|
def chrome_gte?(version, session)
|
40
|
-
chrome?(session) && (session
|
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.
|
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-
|
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
|