capybara 3.15.1 → 3.16.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 +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
|
[![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)
|
7
7
|
[![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)
|
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
|