capybara 3.15.1 → 3.16.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 +9 -4
- data/README.md +2 -2
- data/lib/capybara/helpers.rb +1 -1
- data/lib/capybara/node/actions.rb +1 -1
- data/lib/capybara/node/base.rb +1 -1
- data/lib/capybara/node/finders.rb +1 -1
- data/lib/capybara/queries/base_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +2 -2
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/rspec/matchers.rb +1 -1
- data/lib/capybara/selector.rb +62 -70
- data/lib/capybara/selector/css.rb +2 -2
- data/lib/capybara/selector/xpath_extensions.rb +8 -0
- data/lib/capybara/selenium/driver.rb +23 -39
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +1 -7
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +1 -1
- data/lib/capybara/selenium/extensions/html5_drag.rb +3 -1
- data/lib/capybara/selenium/nodes/chrome_node.rb +2 -10
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/session.rb +8 -4
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/has_css_spec.rb +9 -2
- data/lib/capybara/spec/session/has_table_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/sauce_spec_chrome.rb +42 -0
- data/spec/shared_selenium_session.rb +1 -2
- metadata +33 -4
- data/lib/capybara/selenium/logger_suppressor.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e11c89bad9b8520ba44314b12052ee3b5845a3bd758a8b96bd29b2cda338a1e9
|
4
|
+
data.tar.gz: 8e52ab3d885733f71c1d0803d646890c788ec15cc8169c364c6497a21a299b54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16bcc0e6278f54605652cfe31d98647c0426bba37a1d5695ea7970117de9b9410cfe5d3aa8c154d023979ff24250801d01a26c179aad7ae0acb2124e74e375de
|
7
|
+
data.tar.gz: e49ed2c6600f114a259025d5e05076a1e2b670b68611eed7d60f3304c15a0a9faf7e476b15dd63c46bc552bb145fcdeb45694a0aa1f71459346f851734c538c8
|
data/History.md
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
-
# Version 3.
|
2
|
-
Release date: 2019-
|
1
|
+
# Version 3.16
|
2
|
+
Release date: 2019-03-28
|
3
|
+
|
4
|
+
### Changed
|
5
|
+
|
6
|
+
* Ruby 2.4.0+ is now required
|
7
|
+
* Selenium driver now defaults to using a persistent http client connection
|
3
8
|
|
4
9
|
### Added
|
5
10
|
|
6
|
-
*
|
11
|
+
* :wait option in predicates now accepts `true` to selectively override when `Capybara.predicates_wait == false`
|
7
12
|
|
8
|
-
# Version 3.15
|
13
|
+
# Version 3.15
|
9
14
|
Release date: 2019-03-19
|
10
15
|
|
11
16
|
### Added
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
[](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
7
|
[](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
|
8
8
|
|
9
|
-
**Note** You are viewing the README for the 3.
|
9
|
+
**Note** You are viewing the README for the 3.16.x version of Capybara.
|
10
10
|
|
11
11
|
|
12
12
|
Capybara helps you test web applications by simulating how a real user would
|
@@ -419,7 +419,7 @@ teamcapybara repo once completely stable.
|
|
419
419
|
|
420
420
|
### <a name="capybara-webkit"></a>Capybara-webkit
|
421
421
|
|
422
|
-
Note: `capybara-webkit` depends on QtWebkit which went
|
422
|
+
Note: `capybara-webkit` depends on QtWebkit which went end-of-life quite some time ago. There has been an attempt to revive the project but `capybara-webkit` is not yet (as far as I know) compatible with the revived version of QtWebKit (could be a good open source project for someone) and as such is still limited to an old version of QtWebKit. This means its support for modern JS and CSS is severely limited.
|
423
423
|
|
424
424
|
The [capybara-webkit driver](https://github.com/thoughtbot/capybara-webkit) is for true headless
|
425
425
|
testing. It uses QtWebKit to start a rendering engine process. It can execute JavaScript as well.
|
data/lib/capybara/helpers.rb
CHANGED
@@ -8,7 +8,7 @@ module Capybara
|
|
8
8
|
# and continuously retry finding the element until either the element is found or the time
|
9
9
|
# expires. The length of time +find+ will wait is controlled through {Capybara.default_max_wait_time}
|
10
10
|
#
|
11
|
-
# @option options [false, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
|
11
|
+
# @option options [false, true, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
|
12
12
|
|
13
13
|
##
|
14
14
|
#
|
data/lib/capybara/node/base.rb
CHANGED
@@ -76,7 +76,7 @@ module Capybara
|
|
76
76
|
def synchronize(seconds = nil, errors: nil)
|
77
77
|
return yield if session.synchronized
|
78
78
|
|
79
|
-
seconds = session_options.default_max_wait_time if
|
79
|
+
seconds = session_options.default_max_wait_time if [nil, true].include? seconds
|
80
80
|
session.synchronized = true
|
81
81
|
timer = Capybara::Helpers.timer(expire_in: seconds)
|
82
82
|
begin
|
@@ -13,7 +13,7 @@ module Capybara
|
|
13
13
|
# and continuously retry finding the element until either the element is found or the time
|
14
14
|
# expires. The length of time +find+ will wait is controlled through {Capybara.default_max_wait_time}
|
15
15
|
# and defaults to 2 seconds.
|
16
|
-
# @option options [false, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
|
16
|
+
# @option options [false, true, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
|
17
17
|
#
|
18
18
|
# page.find('#foo').find('.bar')
|
19
19
|
# page.find(:xpath, './/div[contains(., "bar")]')
|
@@ -98,7 +98,7 @@ module Capybara
|
|
98
98
|
|
99
99
|
invalid_names = invalid_keys.map(&:inspect).join(', ')
|
100
100
|
valid_names = valid_keys.map(&:inspect).join(', ')
|
101
|
-
raise ArgumentError, "
|
101
|
+
raise ArgumentError, "Invalid option(s) #{invalid_names}, should be one of #{valid_names}"
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
@@ -271,7 +271,7 @@ module Capybara
|
|
271
271
|
|
272
272
|
def assert_valid_keys
|
273
273
|
unless VALID_MATCH.include?(match)
|
274
|
-
raise ArgumentError, "
|
274
|
+
raise ArgumentError, "Invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}"
|
275
275
|
end
|
276
276
|
|
277
277
|
unhandled_options = @options.keys.reject do |option_name|
|
@@ -284,7 +284,7 @@ module Capybara
|
|
284
284
|
|
285
285
|
invalid_names = unhandled_options.map(&:inspect).join(', ')
|
286
286
|
valid_names = (valid_keys - [:allow_self]).map(&:inspect).join(', ')
|
287
|
-
raise ArgumentError, "
|
287
|
+
raise ArgumentError, "Invalid option(s) #{invalid_names}, should be one of #{valid_names}"
|
288
288
|
end
|
289
289
|
|
290
290
|
def filtered_expression(expr)
|
@@ -107,7 +107,7 @@ module Capybara
|
|
107
107
|
# See {Capybara::Node::Matchers#has_unchecked_field?}
|
108
108
|
|
109
109
|
# RSpec matcher for text content
|
110
|
-
# See {Capybara::
|
110
|
+
# See {Capybara::Node::Matchers#assert_text}
|
111
111
|
def have_text(*args)
|
112
112
|
Matchers::HaveText.new(*args)
|
113
113
|
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -459,100 +459,92 @@ Capybara.add_selector(:table, locator_type: [String, Symbol]) do
|
|
459
459
|
expression_filter(:with_cols, valid_values: [Array]) do |xpath, cols|
|
460
460
|
col_conditions = cols.map do |col|
|
461
461
|
if col.is_a? Hash
|
462
|
-
col.reduce(nil) do |xp, (header,
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
xp = XPath::Expression.new(:join, table_ancestor, xp)
|
469
|
-
cell_xp[XPath.string.n.is(cell) & XPath.position.equals(xp.preceding_sibling(:td).count.plus(1))]
|
462
|
+
col.reduce(nil) do |xp, (header, cell_str)|
|
463
|
+
header = XPath.descendant(:th)[XPath.string.n.is(header)]
|
464
|
+
td = XPath.descendant(:tr)[header].descendant(:td)
|
465
|
+
cell_condition = XPath.string.n.is(cell_str)
|
466
|
+
cell_condition &= prev_col_position?(XPath.ancestor(:table)[1].join(xp)) if xp
|
467
|
+
td[cell_condition]
|
470
468
|
end
|
471
469
|
else
|
472
|
-
cells_xp = col.reduce(nil) do |
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
470
|
+
cells_xp = col.reduce(nil) do |prev_cell, cell_str|
|
471
|
+
cell_condition = XPath.string.n.is(cell_str)
|
472
|
+
|
473
|
+
if prev_cell
|
474
|
+
prev_cell = XPath.ancestor(:tr)[1].preceding_sibling(:tr).join(prev_cell)
|
475
|
+
cell_condition &= prev_col_position?(prev_cell)
|
477
476
|
end
|
478
|
-
|
477
|
+
|
478
|
+
XPath.descendant(:td)[cell_condition]
|
479
479
|
end
|
480
|
-
XPath
|
480
|
+
XPath.descendant(:tr).join(cells_xp)
|
481
481
|
end
|
482
482
|
end.reduce(:&)
|
483
483
|
xpath[col_conditions]
|
484
484
|
end
|
485
485
|
|
486
486
|
expression_filter(:cols, valid_values: [Array]) do |xpath, cols|
|
487
|
-
raise ArgumentError,
|
487
|
+
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all? { |col| col.is_a? Array }
|
488
488
|
|
489
489
|
rows = cols.transpose
|
490
|
-
|
491
|
-
|
492
|
-
col_conditions = rows.map do |row|
|
493
|
-
row_conditions = row.map do |cell|
|
494
|
-
XPath.self(:td)[XPath.string.n.is(cell)]
|
495
|
-
end
|
496
|
-
row_conditions = row_conditions.reverse.reduce do |cond, cell|
|
497
|
-
cell[XPath.following_sibling[cond]]
|
498
|
-
end
|
499
|
-
row_xpath = XPath.descendant(:tr)[XPath.descendant(:td)[row_conditions]]
|
500
|
-
row_xpath[XPath.descendant(:td).count.equals(row.size)]
|
501
|
-
end.reduce(:&)
|
502
|
-
|
503
|
-
xpath[col_conditions]
|
490
|
+
col_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
491
|
+
xpath[match_row_count(rows.size)][col_conditions]
|
504
492
|
end
|
505
493
|
|
506
494
|
expression_filter(:with_rows, valid_values: [Array]) do |xpath, rows|
|
507
|
-
rows_conditions = rows.map
|
508
|
-
if row.is_a? Hash
|
509
|
-
row_conditions = row.map do |header, cell|
|
510
|
-
header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
|
511
|
-
XPath.descendant(:td)[
|
512
|
-
XPath.string.n.is(cell) & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
|
513
|
-
]
|
514
|
-
end.reduce(:&)
|
515
|
-
XPath.descendant(:tr)[row_conditions]
|
516
|
-
else
|
517
|
-
row_conditions = row.map do |cell|
|
518
|
-
XPath.self(:td)[XPath.string.n.is(cell)]
|
519
|
-
end
|
520
|
-
row_conditions = row_conditions.reverse.reduce do |cond, cell|
|
521
|
-
cell[XPath.following_sibling[cond]]
|
522
|
-
end
|
523
|
-
XPath.descendant(:tr)[XPath.descendant(:td)[row_conditions]]
|
524
|
-
end
|
525
|
-
end.reduce(:&)
|
495
|
+
rows_conditions = rows.map { |row| match_row(row) }.reduce(:&)
|
526
496
|
xpath[rows_conditions]
|
527
497
|
end
|
528
498
|
|
529
499
|
expression_filter(:rows, valid_values: [Array]) do |xpath, rows|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
500
|
+
rows_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
501
|
+
xpath[match_row_count(rows.size)][rows_conditions]
|
502
|
+
end
|
503
|
+
|
504
|
+
describe_expression_filters do |caption: nil, **|
|
505
|
+
" with caption \"#{caption}\"" if caption
|
506
|
+
end
|
507
|
+
|
508
|
+
def prev_col_position?(cell)
|
509
|
+
XPath.position.equals(cell_position(cell))
|
510
|
+
end
|
511
|
+
|
512
|
+
def cell_position(cell)
|
513
|
+
cell.preceding_sibling(:td).count.plus(1)
|
514
|
+
end
|
515
|
+
|
516
|
+
def match_row(row, match_size: false)
|
517
|
+
xp = XPath.descendant(:tr)[
|
518
|
+
if row.is_a? Hash
|
519
|
+
row_match_cells_to_headers(row)
|
540
520
|
else
|
541
|
-
|
542
|
-
XPath.self(:td)[XPath.string.n.is(cell)]
|
543
|
-
end
|
544
|
-
row_conditions = row_conditions.reverse.reduce do |cond, cell|
|
545
|
-
cell[XPath.following_sibling[cond]]
|
546
|
-
end
|
547
|
-
XPath.descendant(:tr)[XPath.descendant(:td)[row_conditions]]
|
521
|
+
XPath.descendant(:td)[row_match_ordered_cells(row)]
|
548
522
|
end
|
549
|
-
|
523
|
+
]
|
524
|
+
xp = xp[XPath.descendant(:td).count.equals(row.size)] if match_size
|
525
|
+
xp
|
526
|
+
end
|
527
|
+
|
528
|
+
def match_row_count(size)
|
529
|
+
XPath.descendant(:tbody).descendant(:tr).count.equals(size) | (XPath.descendant(:tr).count.equals(size) & ~XPath.descendant(:tbody))
|
530
|
+
end
|
531
|
+
|
532
|
+
def row_match_cells_to_headers(row)
|
533
|
+
row.map do |header, cell|
|
534
|
+
header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
|
535
|
+
XPath.descendant(:td)[
|
536
|
+
XPath.string.n.is(cell) & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
|
537
|
+
]
|
550
538
|
end.reduce(:&)
|
551
|
-
xpath[rows_conditions]
|
552
539
|
end
|
553
540
|
|
554
|
-
|
555
|
-
|
541
|
+
def row_match_ordered_cells(row)
|
542
|
+
row_conditions = row.map do |cell|
|
543
|
+
XPath.self(:td)[XPath.string.n.is(cell)]
|
544
|
+
end
|
545
|
+
row_conditions.reverse.reduce do |cond, cell|
|
546
|
+
cell[XPath.following_sibling[cond]]
|
547
|
+
end
|
556
548
|
end
|
557
549
|
end
|
558
550
|
|
@@ -7,13 +7,13 @@ module Capybara
|
|
7
7
|
value = str.dup
|
8
8
|
out = +''
|
9
9
|
out << value.slice!(0...1) if value =~ /^[-_]/
|
10
|
-
out << (value[0]
|
10
|
+
out << (value[0].match?(NMSTART) ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
|
11
11
|
out << value.gsub(/[^a-zA-Z0-9_-]/) { |char| escape_char char }
|
12
12
|
out
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.escape_char(char)
|
16
|
-
char
|
16
|
+
char.match?(%r{[ -/:-~]}) ? "\\#{char}" : format('\\%06x', char.ord)
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.split(css)
|
@@ -16,7 +16,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
16
16
|
|
17
17
|
def self.load_selenium
|
18
18
|
require 'selenium-webdriver'
|
19
|
-
require 'capybara/selenium/
|
19
|
+
require 'capybara/selenium/patches/persistent_client'
|
20
20
|
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0')
|
21
21
|
rescue LoadError => err
|
22
22
|
raise err if err.message !~ /selenium-webdriver/
|
@@ -26,8 +26,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
26
26
|
|
27
27
|
def browser
|
28
28
|
@browser ||= begin
|
29
|
-
|
30
|
-
|
29
|
+
options[:http_client] ||= begin
|
30
|
+
if options[:timeout]
|
31
|
+
::Capybara::Selenium::PersistentClient.new(read_timeout: options[:timeout])
|
32
|
+
else
|
33
|
+
::Capybara::Selenium::PersistentClient.new
|
34
|
+
end
|
31
35
|
end
|
32
36
|
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
33
37
|
Selenium::WebDriver.for(options[:browser], processed_options).tap do |driver|
|
@@ -111,7 +115,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
111
115
|
navigated = true
|
112
116
|
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
113
117
|
wait_for_empty_page(timer)
|
114
|
-
rescue
|
118
|
+
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
115
119
|
# This error is thrown if an unhandled alert is on the page
|
116
120
|
# Firefox appears to automatically dismiss this alert, chrome does not
|
117
121
|
# We'll try to accept it
|
@@ -178,7 +182,10 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
178
182
|
browser.window_handles
|
179
183
|
end
|
180
184
|
|
181
|
-
def open_new_window
|
185
|
+
def open_new_window(kind = :tab)
|
186
|
+
browser.manage.new_window(kind)
|
187
|
+
rescue NoMethodError, Selenium::WebDriver::Error::WebDriverError
|
188
|
+
# If not supported by the driver or browser default to using JS
|
182
189
|
browser.execute_script('window.open();')
|
183
190
|
end
|
184
191
|
|
@@ -219,24 +226,19 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
219
226
|
end
|
220
227
|
|
221
228
|
def invalid_element_errors
|
222
|
-
|
229
|
+
[
|
223
230
|
::Selenium::WebDriver::Error::StaleElementReferenceError,
|
231
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
232
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
233
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a chromedriver go_back/go_forward race condition
|
224
234
|
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
225
|
-
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition
|
226
235
|
::Selenium::WebDriver::Error::ElementClickInterceptedError,
|
236
|
+
::Selenium::WebDriver::Error::InvalidElementStateError,
|
237
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
238
|
+
::Selenium::WebDriver::Error::ElementNotSelectableError,
|
227
239
|
::Selenium::WebDriver::Error::NoSuchElementError, # IE
|
228
240
|
::Selenium::WebDriver::Error::InvalidArgumentError # IE
|
229
241
|
]
|
230
|
-
|
231
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
232
|
-
errors.concat [
|
233
|
-
::Selenium::WebDriver::Error::UnhandledError,
|
234
|
-
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
235
|
-
::Selenium::WebDriver::Error::InvalidElementStateError,
|
236
|
-
::Selenium::WebDriver::Error::ElementNotSelectableError
|
237
|
-
]
|
238
|
-
end
|
239
|
-
errors
|
240
242
|
end
|
241
243
|
|
242
244
|
def no_such_window_error
|
@@ -252,24 +254,12 @@ private
|
|
252
254
|
def clear_browser_state
|
253
255
|
delete_all_cookies
|
254
256
|
clear_storage
|
255
|
-
rescue
|
257
|
+
rescue Selenium::WebDriver::Error::UnhandledError # rubocop:disable Lint/HandleExceptions
|
256
258
|
# delete_all_cookies fails when we've previously gone
|
257
259
|
# to about:blank, so we rescue this error and do nothing
|
258
260
|
# instead.
|
259
261
|
end
|
260
262
|
|
261
|
-
def clear_browser_state_errors
|
262
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
263
|
-
[Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::UnknownError]
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def unhandled_alert_errors
|
268
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
269
|
-
[Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError]
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
263
|
def delete_all_cookies
|
274
264
|
@browser.manage.delete_all_cookies
|
275
265
|
end
|
@@ -340,19 +330,13 @@ private
|
|
340
330
|
wait.until do
|
341
331
|
alert = @browser.switch_to.alert
|
342
332
|
regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s)
|
343
|
-
alert.text.match(regexp) ? alert : nil
|
333
|
+
alert.text.match?(regexp) ? alert : nil
|
344
334
|
end
|
345
|
-
rescue
|
335
|
+
rescue Selenium::WebDriver::Error::TimeOutError
|
346
336
|
raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}"
|
347
337
|
end
|
348
338
|
end
|
349
339
|
|
350
|
-
def find_modal_errors
|
351
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
352
|
-
[Selenium::WebDriver::Error::TimeoutError, Selenium::WebDriver::Error::TimeOutError]
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
340
|
def silenced_unknown_error_message?(msg)
|
357
341
|
silenced_unknown_error_messages.any? { |regex| msg =~ regex }
|
358
342
|
end
|
@@ -366,7 +350,7 @@ private
|
|
366
350
|
when Array
|
367
351
|
arg.map { |arr| unwrap_script_result(arr) }
|
368
352
|
when Hash
|
369
|
-
arg.
|
353
|
+
arg.transform_values! { |value| unwrap_script_result(value) }
|
370
354
|
when Selenium::WebDriver::Element
|
371
355
|
build_node(arg)
|
372
356
|
else
|
@@ -40,17 +40,11 @@ private
|
|
40
40
|
|
41
41
|
def delete_all_cookies
|
42
42
|
execute_cdp('Network.clearBrowserCookies')
|
43
|
-
rescue
|
43
|
+
rescue Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::WebDriverError
|
44
44
|
# If the CDP clear isn't supported do original limited clear
|
45
45
|
super
|
46
46
|
end
|
47
47
|
|
48
|
-
def cdp_unsupported_errors
|
49
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
50
|
-
[Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::WebDriverError]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
48
|
def execute_cdp(cmd, params = {})
|
55
49
|
args = { cmd: cmd, params: params }
|
56
50
|
result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'capybara/selenium/nodes/safari_node'
|
4
4
|
|
5
5
|
module Capybara::Selenium::Driver::SafariDriver
|
6
|
-
private
|
6
|
+
private # rubocop:disable Layout/IndentationWidth
|
7
7
|
|
8
8
|
def build_node(native_node, initial_cache = {})
|
9
9
|
::Capybara::Selenium::SafariNode.new(self, native_node, initial_cache)
|
@@ -14,10 +14,8 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
14
14
|
|
15
15
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
16
16
|
super(value)
|
17
|
-
rescue
|
18
|
-
if err.message
|
19
|
-
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if e.message.match?(/File not found : .+\n.+/m)
|
20
|
-
end
|
17
|
+
rescue ::Selenium::WebDriver::Error::ExpectedError => err
|
18
|
+
raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" if err.message.match?(/File not found : .+\n.+/m)
|
21
19
|
|
22
20
|
raise
|
23
21
|
end
|
@@ -30,12 +28,6 @@ class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
|
30
28
|
|
31
29
|
private
|
32
30
|
|
33
|
-
def file_errors
|
34
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
35
|
-
[::Selenium::WebDriver::Error::ExpectedError]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
31
|
def bridge
|
40
32
|
driver.browser.send(:bridge)
|
41
33
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
module Selenium
|
5
|
+
class PersistentClient < ::Selenium::WebDriver::Remote::Http::Default
|
6
|
+
def close
|
7
|
+
super
|
8
|
+
@http.finish if @http&.started?
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def http
|
14
|
+
super.tap do |http|
|
15
|
+
http.start unless http.started?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/capybara/session.rb
CHANGED
@@ -263,7 +263,7 @@ module Capybara
|
|
263
263
|
|
264
264
|
if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
265
265
|
if visit_uri.relative?
|
266
|
-
visit_uri_parts = visit_uri.to_hash.
|
266
|
+
visit_uri_parts = visit_uri.to_hash.compact
|
267
267
|
|
268
268
|
# Useful to people deploying to a subdirectory
|
269
269
|
# and/or single page apps where only the url fragment changes
|
@@ -458,9 +458,13 @@ module Capybara
|
|
458
458
|
#
|
459
459
|
# @return [Capybara::Window] window that has been opened
|
460
460
|
#
|
461
|
-
def open_new_window
|
461
|
+
def open_new_window(kind = :tab)
|
462
462
|
window_opened_by do
|
463
|
-
driver.open_new_window
|
463
|
+
if driver.method(:open_new_window).arity.zero?
|
464
|
+
driver.open_new_window
|
465
|
+
else
|
466
|
+
driver.open_new_window(kind)
|
467
|
+
end
|
464
468
|
end
|
465
469
|
end
|
466
470
|
|
@@ -850,7 +854,7 @@ module Capybara
|
|
850
854
|
when Array
|
851
855
|
arg.map { |subarg| element_script_result(subarg) }
|
852
856
|
when Hash
|
853
|
-
arg.
|
857
|
+
arg.transform_values! { |value| element_script_result(value) }
|
854
858
|
when Capybara::Driver::Node
|
855
859
|
Capybara::Node::Element.new(self, arg, nil, nil)
|
856
860
|
else
|
@@ -108,7 +108,7 @@ Capybara::SpecHelper.spec '#find' do
|
|
108
108
|
|
109
109
|
it 'should warn if passed a non-valid locator type' do
|
110
110
|
expect_any_instance_of(Kernel).to receive(:warn).with(/must respond to to_xpath or be an instance of String/)
|
111
|
-
expect { @session.find(:xpath, 123) }.to raise_error #
|
111
|
+
expect { @session.find(:xpath, 123) }.to raise_error Exception # The exact error is not yet well defined
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
@@ -113,13 +113,20 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
113
113
|
end
|
114
114
|
|
115
115
|
context 'with predicates_wait == false' do
|
116
|
-
|
116
|
+
before do
|
117
117
|
Capybara.predicates_wait = false
|
118
|
-
Capybara.default_max_wait_time =
|
118
|
+
Capybara.default_max_wait_time = 5
|
119
119
|
@session.visit('/with_js')
|
120
120
|
@session.click_link('Click me')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should not wait for content to appear', requires: [:js] do
|
121
124
|
expect(@session.has_css?("input[type='submit'][value='New Here']")).to be false
|
122
125
|
end
|
126
|
+
|
127
|
+
it 'should should the default wait time if true is passed for :wait', requires: [:js] do
|
128
|
+
expect(@session.has_css?("input[type='submit'][value='New Here']", wait: true)).to be true
|
129
|
+
end
|
123
130
|
end
|
124
131
|
|
125
132
|
context 'with between' do
|
@@ -49,8 +49,8 @@ Capybara::SpecHelper.spec '#has_table?' do
|
|
49
49
|
%w[Thomas Walpole Oceanside],
|
50
50
|
%w[Danilo Wilkinson Johnsonville],
|
51
51
|
%w[Vern Konopelski Everette],
|
52
|
-
[
|
53
|
-
[
|
52
|
+
['Ratke', 'Lawrence', 'East Sorayashire'],
|
53
|
+
['Palmer', 'Sawayn', 'West Trinidad']
|
54
54
|
])
|
55
55
|
end
|
56
56
|
|
@@ -84,8 +84,8 @@ Capybara::SpecHelper.spec '#has_table?' do
|
|
84
84
|
%w[Thomas Walpole Oceanside],
|
85
85
|
%w[Danilo Wilkinson Johnsonville],
|
86
86
|
%w[Vern Konopelski Everette],
|
87
|
-
[
|
88
|
-
[
|
87
|
+
['Ratke', 'Lawrence', 'East Sorayashire'],
|
88
|
+
['Palmer', 'Sawayn', 'West Trinidad']
|
89
89
|
])
|
90
90
|
end
|
91
91
|
|
@@ -100,7 +100,7 @@ module Capybara
|
|
100
100
|
|
101
101
|
def silence_stream(stream)
|
102
102
|
old_stream = stream.dup
|
103
|
-
stream.reopen(RbConfig::CONFIG['host_os']
|
103
|
+
stream.reopen(RbConfig::CONFIG['host_os'].match?(/rmswin|mingw/) ? 'NUL:' : '/dev/null')
|
104
104
|
stream.sync = true
|
105
105
|
yield
|
106
106
|
ensure
|
data/lib/capybara/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'selenium-webdriver'
|
5
|
+
|
6
|
+
require 'sauce_whisk'
|
7
|
+
# require 'shared_selenium_session'
|
8
|
+
# require 'rspec/shared_spec_matchers'
|
9
|
+
|
10
|
+
Capybara.register_driver :sauce_chrome do |app|
|
11
|
+
options = {
|
12
|
+
selenium_version: '3.141.59',
|
13
|
+
platform: 'macOS 10.12',
|
14
|
+
browser_name: 'chrome',
|
15
|
+
version: '65.0',
|
16
|
+
name: 'Capybara test',
|
17
|
+
build: ENV['TRAVIS_REPO_SLUG'] || "Ruby-RSpec-Selenium: Local-#{Time.now.to_i}",
|
18
|
+
username: ENV['SAUCE_USERNAME'],
|
19
|
+
access_key: ENV['SAUCE_ACCESS_KEY']
|
20
|
+
}
|
21
|
+
|
22
|
+
options.delete(:browser_name)
|
23
|
+
|
24
|
+
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(options)
|
25
|
+
url = 'https://ondemand.saucelabs.com:443/wd/hub'
|
26
|
+
|
27
|
+
Capybara::Selenium::Driver.new(app,
|
28
|
+
browser: :remote, url: url,
|
29
|
+
desired_capabilities: capabilities,
|
30
|
+
options: Selenium::WebDriver::Chrome::Options.new(args: ['']))
|
31
|
+
end
|
32
|
+
|
33
|
+
CHROME_REMOTE_DRIVER = :sauce_chrome
|
34
|
+
|
35
|
+
module TestSessions
|
36
|
+
Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp)
|
37
|
+
end
|
38
|
+
|
39
|
+
skipped_tests = %i[response_headers status_code trigger download]
|
40
|
+
|
41
|
+
Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example|
|
42
|
+
end
|
@@ -474,8 +474,7 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|
474
474
|
it 'can set and clear a text field' do
|
475
475
|
session.visit 'https://reactjs.org/docs/forms.html'
|
476
476
|
session.all(:css, 'h2#controlled-components ~ p a', text: 'Try it on CodePen')[0].click
|
477
|
-
|
478
|
-
session.within_frame(:css, 'iframe.result-iframe') do
|
477
|
+
session.within_frame(:css, 'iframe.result-iframe[src^="https://s.codepen.io"]', wait: 10) do
|
479
478
|
session.fill_in('Name:', with: 'abc')
|
480
479
|
session.accept_prompt 'A name was submitted: abc' do
|
481
480
|
session.click_button('Submit')
|
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.16.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-03-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: addressable
|
@@ -264,6 +264,20 @@ dependencies:
|
|
264
264
|
- - ">="
|
265
265
|
- !ruby/object:Gem::Version
|
266
266
|
version: '0'
|
267
|
+
- !ruby/object:Gem::Dependency
|
268
|
+
name: rubocop-performance
|
269
|
+
requirement: !ruby/object:Gem::Requirement
|
270
|
+
requirements:
|
271
|
+
- - ">="
|
272
|
+
- !ruby/object:Gem::Version
|
273
|
+
version: '0'
|
274
|
+
type: :development
|
275
|
+
prerelease: false
|
276
|
+
version_requirements: !ruby/object:Gem::Requirement
|
277
|
+
requirements:
|
278
|
+
- - ">="
|
279
|
+
- !ruby/object:Gem::Version
|
280
|
+
version: '0'
|
267
281
|
- !ruby/object:Gem::Dependency
|
268
282
|
name: rubocop-rspec
|
269
283
|
requirement: !ruby/object:Gem::Requirement
|
@@ -278,6 +292,20 @@ dependencies:
|
|
278
292
|
- - ">="
|
279
293
|
- !ruby/object:Gem::Version
|
280
294
|
version: '0'
|
295
|
+
- !ruby/object:Gem::Dependency
|
296
|
+
name: sauce_whisk
|
297
|
+
requirement: !ruby/object:Gem::Requirement
|
298
|
+
requirements:
|
299
|
+
- - ">="
|
300
|
+
- !ruby/object:Gem::Version
|
301
|
+
version: '0'
|
302
|
+
type: :development
|
303
|
+
prerelease: false
|
304
|
+
version_requirements: !ruby/object:Gem::Requirement
|
305
|
+
requirements:
|
306
|
+
- - ">="
|
307
|
+
- !ruby/object:Gem::Version
|
308
|
+
version: '0'
|
281
309
|
- !ruby/object:Gem::Dependency
|
282
310
|
name: selenium-webdriver
|
283
311
|
requirement: !ruby/object:Gem::Requirement
|
@@ -414,12 +442,12 @@ files:
|
|
414
442
|
- lib/capybara/selenium/extensions/find.rb
|
415
443
|
- lib/capybara/selenium/extensions/html5_drag.rb
|
416
444
|
- lib/capybara/selenium/extensions/scroll.rb
|
417
|
-
- lib/capybara/selenium/logger_suppressor.rb
|
418
445
|
- lib/capybara/selenium/node.rb
|
419
446
|
- lib/capybara/selenium/nodes/chrome_node.rb
|
420
447
|
- lib/capybara/selenium/nodes/firefox_node.rb
|
421
448
|
- lib/capybara/selenium/nodes/safari_node.rb
|
422
449
|
- lib/capybara/selenium/patches/pause_duration_fix.rb
|
450
|
+
- lib/capybara/selenium/patches/persistent_client.rb
|
423
451
|
- lib/capybara/server.rb
|
424
452
|
- lib/capybara/server/animation_disabler.rb
|
425
453
|
- lib/capybara/server/checker.rb
|
@@ -585,6 +613,7 @@ files:
|
|
585
613
|
- spec/rspec/views_spec.rb
|
586
614
|
- spec/rspec_matchers_spec.rb
|
587
615
|
- spec/rspec_spec.rb
|
616
|
+
- spec/sauce_spec_chrome.rb
|
588
617
|
- spec/selector_spec.rb
|
589
618
|
- spec/selenium_spec_chrome.rb
|
590
619
|
- spec/selenium_spec_chrome_remote.rb
|
@@ -612,7 +641,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
612
641
|
requirements:
|
613
642
|
- - ">="
|
614
643
|
- !ruby/object:Gem::Version
|
615
|
-
version: 2.
|
644
|
+
version: 2.4.0
|
616
645
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
617
646
|
requirements:
|
618
647
|
- - ">="
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Capybara
|
4
|
-
module Selenium
|
5
|
-
module DeprecationSuppressor
|
6
|
-
def deprecate(*)
|
7
|
-
super unless @suppress_for_capybara
|
8
|
-
end
|
9
|
-
|
10
|
-
def suppress_deprecations
|
11
|
-
prev_suppress_for_capybara, @suppress_for_capybara = @suppress_for_capybara, true
|
12
|
-
yield
|
13
|
-
ensure
|
14
|
-
@suppress_for_capybara = prev_suppress_for_capybara
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module ErrorSuppressor
|
19
|
-
def for_code(*)
|
20
|
-
::Selenium::WebDriver.logger.suppress_deprecations do
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Selenium::WebDriver::Logger.prepend Capybara::Selenium::DeprecationSuppressor
|
29
|
-
Selenium::WebDriver::Error.singleton_class.prepend Capybara::Selenium::ErrorSuppressor
|