capybara 3.22.0 → 3.23.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![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.
|
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
|