capybara 3.12.0 → 3.13.2
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 +27 -0
- data/README.md +13 -3
- data/lib/capybara/config.rb +3 -1
- data/lib/capybara/driver/base.rb +2 -2
- data/lib/capybara/driver/node.rb +11 -2
- data/lib/capybara/minitest/spec.rb +10 -3
- data/lib/capybara/minitest.rb +3 -3
- data/lib/capybara/node/actions.rb +4 -0
- data/lib/capybara/node/base.rb +13 -5
- data/lib/capybara/node/document.rb +12 -0
- data/lib/capybara/node/element.rb +37 -0
- data/lib/capybara/node/finders.rb +1 -0
- data/lib/capybara/node/matchers.rb +19 -4
- data/lib/capybara/node/simple.rb +7 -2
- data/lib/capybara/queries/selector_query.rb +93 -9
- data/lib/capybara/rspec/matchers/become_closed.rb +1 -1
- data/lib/capybara/rspec/matchers/match_style.rb +38 -0
- data/lib/capybara/rspec/matchers.rb +11 -3
- data/lib/capybara/selector/selector.rb +47 -3
- data/lib/capybara/selector.rb +30 -30
- data/lib/capybara/selenium/driver.rb +12 -11
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -3
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +13 -0
- data/lib/capybara/selenium/extensions/find.rb +87 -0
- data/lib/capybara/selenium/extensions/scroll.rb +78 -0
- data/lib/capybara/selenium/node.rb +52 -28
- data/lib/capybara/session.rb +13 -1
- data/lib/capybara/spec/public/test.js +1 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +4 -4
- data/lib/capybara/spec/session/attach_file_spec.rb +6 -6
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +1 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +7 -1
- data/lib/capybara/spec/session/find_field_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +11 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +0 -1
- data/lib/capybara/spec/session/has_css_spec.rb +32 -0
- data/lib/capybara/spec/session/has_select_spec.rb +2 -2
- data/lib/capybara/spec/session/has_selector_spec.rb +7 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +5 -0
- data/lib/capybara/spec/spec_helper.rb +1 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/lib/capybara.rb +8 -4
- data/spec/basic_node_spec.rb +11 -0
- data/spec/dsl_spec.rb +3 -1
- data/spec/minitest_spec.rb +2 -2
- data/spec/minitest_spec_spec.rb +1 -1
- data/spec/rack_test_spec.rb +4 -1
- data/spec/result_spec.rb +2 -2
- data/spec/selector_spec.rb +11 -1
- data/spec/selenium_spec_firefox.rb +1 -1
- data/spec/selenium_spec_ie.rb +70 -9
- data/spec/session_spec.rb +9 -0
- data/spec/shared_selenium_session.rb +4 -3
- data/spec/spec_helper.rb +2 -0
- metadata +38 -6
- data/lib/capybara/rspec/matchers/have_style.rb +0 -23
- data/lib/capybara/spec/session/has_style_spec.rb +0 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 535d547d5a25d769afa754066f54cc0d873a2997ebfbc1ed12dd418d80cbb3f0
|
|
4
|
+
data.tar.gz: c576db6d285f055bfc3e030ff4973f7c3fecaf54bff9e74c64070408a2a579ac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37be4e1475274ec82574b4839eb7a06b4db262ebacbd6534669561b92b4a87fcbfc73d57d058657a58b800ec95b86804d16ea663beb4407c6cb9247af100e283
|
|
7
|
+
data.tar.gz: b6b5996ff037790502bb89783f48441c6288e5241b967739a463dcf339c17bc1d3a2c5c4226bf7ab3eb502b82e56079347c96c5a55737edcd84477a85a4fa92e
|
data/History.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
# Version 3.13.2
|
|
2
|
+
Release date: 2019-01-24
|
|
3
|
+
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
* Remove extraneous output
|
|
7
|
+
|
|
8
|
+
# Version 3.13.1
|
|
9
|
+
Release date: 2019-01-24
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
* Only use Selenium visibility optimization when JS atom is available - Issue #2151
|
|
14
|
+
|
|
15
|
+
# Version 3.13.0
|
|
16
|
+
Release date: 2019-01-23
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
* Session#quit added
|
|
21
|
+
* #scroll_to added to allow scrolling page/elements to specified locations
|
|
22
|
+
* Speed optimizations around multiple element location and path generation when using the Selenium driver
|
|
23
|
+
* Support for locator type checking in custom selectors
|
|
24
|
+
* Allow configuration of gumbo use - defaults to off
|
|
25
|
+
* `assert_style`/`has_style`/`have_style` depreacted in favor of `assert_matches_style`/`matches_styles?`/`match_style`
|
|
26
|
+
* :style filter added to selectors
|
|
27
|
+
|
|
1
28
|
# Version 3.12.0
|
|
2
29
|
Release date: 2018-11-28
|
|
3
30
|
|
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 version 3.13.x of Capybara.
|
|
10
10
|
|
|
11
11
|
Capybara helps you test web applications by simulating how a real user would
|
|
12
12
|
interact with your app. It is agnostic about the driver running your tests and
|
|
@@ -35,6 +35,7 @@ GitHub): http://groups.google.com/group/ruby-capybara
|
|
|
35
35
|
- [Selecting the Driver](#selecting-the-driver)
|
|
36
36
|
- [RackTest](#racktest)
|
|
37
37
|
- [Selenium](#selenium)
|
|
38
|
+
- [Apparition](#apparition)
|
|
38
39
|
- [Capybara-webkit](#capybara-webkit)
|
|
39
40
|
- [Poltergeist](#poltergeist)
|
|
40
41
|
- [The DSL](#the-dsl)
|
|
@@ -217,7 +218,7 @@ feature "Signing in" do
|
|
|
217
218
|
end
|
|
218
219
|
```
|
|
219
220
|
|
|
220
|
-
`feature` is in fact just an alias for `describe ..., type
|
|
221
|
+
`feature` is in fact just an alias for `describe ..., type: :feature`,
|
|
221
222
|
`background` is an alias for `before`, `scenario` for `it`, and
|
|
222
223
|
`given`/`given!` aliases for `let`/`let!`, respectively.
|
|
223
224
|
|
|
@@ -406,6 +407,15 @@ to the browsers. See the section on adding and configuring drivers.
|
|
|
406
407
|
same transaction as your tests, causing data not to be shared between your test
|
|
407
408
|
and test server, see [Transactions and database setup](#transactions-and-database-setup) below.
|
|
408
409
|
|
|
410
|
+
### <a name="apparition"></a>Apparition
|
|
411
|
+
|
|
412
|
+
The [apparition driver](https://github.com/twalpole/apparition) in a new driver thatallows you to run tests using Chrome in a headless
|
|
413
|
+
or headed configuration. It attempts to provide backwards compatibility with the [Poltergeist driver API](https://github.com/teampoltergeist/poltergeist)
|
|
414
|
+
while allowing for the use of modern JS/CSS. It uses CDP to communicate with Chrome, thereby obviating the need for chromedriver.
|
|
415
|
+
A compatibility layer for capybara-webkit is planned, although has not yet been started. This driver is being developed by the
|
|
416
|
+
maintainer of Capybara and will attempt to keep up to date with new Capybara releases. It will probably be moved into the
|
|
417
|
+
teamcapybara repo once completely stable.
|
|
418
|
+
|
|
409
419
|
### <a name="capybara-webkit"></a>Capybara-webkit
|
|
410
420
|
|
|
411
421
|
Note: `capybara-webkit` depends on QtWebkit which went EOL quite some time ago. There has been an attempt to revive the project but `capybara-webkit` is not yet (AFAIK) compatible with the revived version of QtWebKit (could be a good OSS 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.
|
|
@@ -428,7 +438,7 @@ Capybara.javascript_driver = :webkit
|
|
|
428
438
|
|
|
429
439
|
### <a name="poltergeist"></a>Poltergeist
|
|
430
440
|
|
|
431
|
-
Note: `poltergeist` depends on PhantomJS for which active development ended quite some time ago (2.1.1). As such it is roughly equivalent to a 6-7 year old version of Safari, meaning lack of support for modern JS
|
|
441
|
+
Note: `poltergeist` depends on PhantomJS for which active development ended quite some time ago (2.1.1). As such it is roughly equivalent to a 6-7 year old version of Safari, meaning lack of support for modern JS and CSS. If any effort to update PhantomJS succeeds in the future this situation could change.
|
|
432
442
|
|
|
433
443
|
[Poltergeist](https://github.com/teampoltergeist/poltergeist) is another
|
|
434
444
|
headless driver which integrates Capybara with
|
data/lib/capybara/config.rb
CHANGED
|
@@ -7,12 +7,14 @@ module Capybara
|
|
|
7
7
|
class Config
|
|
8
8
|
extend Forwardable
|
|
9
9
|
|
|
10
|
-
OPTIONS = %i[app reuse_server threadsafe default_wait_time server
|
|
10
|
+
OPTIONS = %i[app reuse_server threadsafe default_wait_time server
|
|
11
|
+
default_driver javascript_driver allow_gumbo].freeze
|
|
11
12
|
|
|
12
13
|
attr_accessor :app
|
|
13
14
|
attr_reader :reuse_server, :threadsafe
|
|
14
15
|
attr_reader :session_options
|
|
15
16
|
attr_writer :default_driver, :javascript_driver
|
|
17
|
+
attr_accessor :allow_gumbo
|
|
16
18
|
|
|
17
19
|
SessionConfig::OPTIONS.each do |method|
|
|
18
20
|
def_delegators :session_options, method, "#{method}="
|
data/lib/capybara/driver/base.rb
CHANGED
|
@@ -15,11 +15,11 @@ class Capybara::Driver::Base
|
|
|
15
15
|
raise NotImplementedError
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def find_xpath(query)
|
|
18
|
+
def find_xpath(query, **options)
|
|
19
19
|
raise NotImplementedError
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def find_css(query)
|
|
22
|
+
def find_css(query, **options)
|
|
23
23
|
raise NotImplementedError
|
|
24
24
|
end
|
|
25
25
|
|
data/lib/capybara/driver/node.rb
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
module Capybara
|
|
4
4
|
module Driver
|
|
5
5
|
class Node
|
|
6
|
-
attr_reader :driver, :native
|
|
6
|
+
attr_reader :driver, :native, :initial_cache
|
|
7
7
|
|
|
8
|
-
def initialize(driver, native)
|
|
8
|
+
def initialize(driver, native, initial_cache = {})
|
|
9
9
|
@driver = driver
|
|
10
10
|
@native = native
|
|
11
|
+
@initial_cache = initial_cache
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def all_text
|
|
@@ -68,6 +69,14 @@ module Capybara
|
|
|
68
69
|
raise NotImplementedError
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
def scroll_by(x, y)
|
|
73
|
+
raise NotImplementedError
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def scroll_to(element, alignment, position = nil)
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
end
|
|
79
|
+
|
|
71
80
|
def tag_name
|
|
72
81
|
raise NotImplementedError
|
|
73
82
|
end
|
|
@@ -17,7 +17,7 @@ module Capybara
|
|
|
17
17
|
end + [%w[assert_all_of_selectors must_have_all_of_selectors],
|
|
18
18
|
%w[assert_none_of_selectors must_have_none_of_selectors],
|
|
19
19
|
%w[assert_any_of_selectors must_have_any_of_selectors],
|
|
20
|
-
%w[
|
|
20
|
+
%w[assert_matches_style must_match_style]] +
|
|
21
21
|
%w[selector xpath css].flat_map do |assertion|
|
|
22
22
|
[%W[assert_matches_#{assertion} must_match_#{assertion}],
|
|
23
23
|
%W[refute_matches_#{assertion} wont_match_#{assertion}]]
|
|
@@ -36,6 +36,13 @@ module Capybara
|
|
|
36
36
|
end
|
|
37
37
|
# rubocop:enable Style/MultilineBlockChain
|
|
38
38
|
|
|
39
|
+
##
|
|
40
|
+
# @deprecated
|
|
41
|
+
def must_have_style(*args, &block)
|
|
42
|
+
warn 'must_have_style is deprecated, please use must_match_style'
|
|
43
|
+
must_match_style(*args, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
39
46
|
##
|
|
40
47
|
# Expectation that there is xpath
|
|
41
48
|
#
|
|
@@ -169,8 +176,8 @@ module Capybara
|
|
|
169
176
|
##
|
|
170
177
|
# Expectation that element has style
|
|
171
178
|
#
|
|
172
|
-
# @!method
|
|
173
|
-
# see {Capybara::Node::Matchers#
|
|
179
|
+
# @!method must_match_style
|
|
180
|
+
# see {Capybara::Node::Matchers#assert_matches_style}
|
|
174
181
|
end
|
|
175
182
|
end
|
|
176
183
|
end
|
data/lib/capybara/minitest.rb
CHANGED
|
@@ -83,10 +83,10 @@ module Capybara
|
|
|
83
83
|
|
|
84
84
|
## Assert element has the provided CSS styles
|
|
85
85
|
#
|
|
86
|
-
# @!method
|
|
87
|
-
# see {Capybara::Node::Matchers#
|
|
86
|
+
# @!method assert_matches_style
|
|
87
|
+
# see {Capybara::Node::Matchers#assert_matches_style}
|
|
88
88
|
|
|
89
|
-
%w[selector no_selector
|
|
89
|
+
%w[selector no_selector matches_style
|
|
90
90
|
all_of_selectors none_of_selectors any_of_selectors
|
|
91
91
|
matches_selector not_matches_selector].each do |assertion_name|
|
|
92
92
|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
|
@@ -195,6 +195,8 @@ module Capybara
|
|
|
195
195
|
#
|
|
196
196
|
# @return [Capybara::Node::Element] The option element selected
|
|
197
197
|
def select(value = nil, from: nil, **options)
|
|
198
|
+
raise ArgumentError, 'The :from option does not take an element' if from.is_a? Capybara::Node::Element
|
|
199
|
+
|
|
198
200
|
el = from ? find_select_or_datalist_input(from, options) : self
|
|
199
201
|
|
|
200
202
|
if el.respond_to?(:tag_name) && (el.tag_name == 'input')
|
|
@@ -221,6 +223,8 @@ module Capybara
|
|
|
221
223
|
#
|
|
222
224
|
# @return [Capybara::Node::Element] The option element unselected
|
|
223
225
|
def unselect(value = nil, from: nil, **options)
|
|
226
|
+
raise ArgumentError, 'The :from option does not take an element' if from.is_a? Capybara::Node::Element
|
|
227
|
+
|
|
224
228
|
scope = from ? find(:select, from, options) : self
|
|
225
229
|
scope.find(:option, value, options).unselect_option
|
|
226
230
|
end
|
data/lib/capybara/node/base.rb
CHANGED
|
@@ -86,7 +86,7 @@ module Capybara
|
|
|
86
86
|
raise err unless driver.wait? && catch_error?(err, errors)
|
|
87
87
|
raise err if timer.expired?
|
|
88
88
|
|
|
89
|
-
sleep(0.
|
|
89
|
+
sleep(0.01)
|
|
90
90
|
reload if session_options.automatic_reload
|
|
91
91
|
retry
|
|
92
92
|
ensure
|
|
@@ -95,13 +95,21 @@ module Capybara
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
# @api private
|
|
98
|
-
def find_css(css)
|
|
99
|
-
base.find_css
|
|
98
|
+
def find_css(css, **options)
|
|
99
|
+
if base.method(:find_css).arity != 1
|
|
100
|
+
base.find_css(css, **options)
|
|
101
|
+
else
|
|
102
|
+
base.find_css(css)
|
|
103
|
+
end
|
|
100
104
|
end
|
|
101
105
|
|
|
102
106
|
# @api private
|
|
103
|
-
def find_xpath(xpath)
|
|
104
|
-
base.find_xpath
|
|
107
|
+
def find_xpath(xpath, **options)
|
|
108
|
+
if base.method(:find_xpath).arity != 1
|
|
109
|
+
base.find_xpath(xpath, **options)
|
|
110
|
+
else
|
|
111
|
+
base.find_xpath(xpath)
|
|
112
|
+
end
|
|
105
113
|
end
|
|
106
114
|
|
|
107
115
|
# @api private
|
|
@@ -31,6 +31,18 @@ module Capybara
|
|
|
31
31
|
def title
|
|
32
32
|
session.driver.title
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
def execute_script(*args)
|
|
36
|
+
find(:xpath, '/html').execute_script(*args)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def evaluate_script(*args)
|
|
40
|
+
find(:xpath, '/html').evaluate_script(*args)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def scroll_to(*args)
|
|
44
|
+
find(:xpath, '//body').scroll_to(*args)
|
|
45
|
+
end
|
|
34
46
|
end
|
|
35
47
|
end
|
|
36
48
|
end
|
|
@@ -381,6 +381,38 @@ module Capybara
|
|
|
381
381
|
self
|
|
382
382
|
end
|
|
383
383
|
|
|
384
|
+
##
|
|
385
|
+
#
|
|
386
|
+
# Scroll the page or element
|
|
387
|
+
#
|
|
388
|
+
# Scroll the page or element to its top, bottom or middle
|
|
389
|
+
# @overload scroll_to(position, offset: [0,0])
|
|
390
|
+
# @param [:top, :bottom, :center, :current] position
|
|
391
|
+
# @param :offset
|
|
392
|
+
#
|
|
393
|
+
# Scroll the page or current element until the given element is aligned at the top, bottom, or center of it
|
|
394
|
+
# @overload scroll_to(element, align: :top)
|
|
395
|
+
# @param [Capybara::Node::Element] element The element to be scrolled into view
|
|
396
|
+
# @param [:top, :bottom, :center] :align Where to align the element being scrolled into view with relation to the current page/element if possible
|
|
397
|
+
#
|
|
398
|
+
# @overload scroll_to(x,y)
|
|
399
|
+
# @param [Integer] x Horizontal scroll offset
|
|
400
|
+
# @param [Integer] y Vertical scroll offset
|
|
401
|
+
#
|
|
402
|
+
# @return [Capybara::Node::Element] The element
|
|
403
|
+
def scroll_to(pos_or_el_or_x, y = nil, align: :top, offset: nil)
|
|
404
|
+
case pos_or_el_or_x
|
|
405
|
+
when Symbol
|
|
406
|
+
synchronize { base.scroll_to(nil, pos_or_el_or_x) } unless pos_or_el_or_x == :current
|
|
407
|
+
when Capybara::Node::Element
|
|
408
|
+
synchronize { base.scroll_to(pos_or_el_or_x.base, align) }
|
|
409
|
+
else
|
|
410
|
+
synchronize { base.scroll_to(nil, nil, [pos_or_el_or_x, y]) }
|
|
411
|
+
end
|
|
412
|
+
synchronize { base.scroll_by(*offset) } unless offset.nil?
|
|
413
|
+
self
|
|
414
|
+
end
|
|
415
|
+
|
|
384
416
|
##
|
|
385
417
|
#
|
|
386
418
|
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
|
@@ -454,6 +486,11 @@ module Capybara
|
|
|
454
486
|
%(Obsolete #<Capybara::Node::Element>)
|
|
455
487
|
end
|
|
456
488
|
|
|
489
|
+
# @api private
|
|
490
|
+
def initial_cache
|
|
491
|
+
base.respond_to?(:initial_cache) ? base.initial_cache : {}
|
|
492
|
+
end
|
|
493
|
+
|
|
457
494
|
STYLE_SCRIPT = <<~JS
|
|
458
495
|
(function(){
|
|
459
496
|
var s = window.getComputedStyle(this);
|
|
@@ -36,6 +36,7 @@ module Capybara
|
|
|
36
36
|
# + Absence of a class can be checked by prefixing the class name with !
|
|
37
37
|
# + If you need to check for existence of a class name that starts with ! then prefix with !!
|
|
38
38
|
# + ```class:['a', '!b', '!!!c'] # limit to elements with class 'a' and '!c' but not class 'b'```
|
|
39
|
+
# @option options [String, Regexp, Hash] style Only find elements with matching style. String and Regexp will be checked against text of the elements `style` attribute, while a Hash will be compared against the elements full style
|
|
39
40
|
# @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
|
|
40
41
|
# @option options [Symbol] match The matching strategy to use.
|
|
41
42
|
#
|
|
@@ -56,13 +56,21 @@ module Capybara
|
|
|
56
56
|
#
|
|
57
57
|
# Checks if a an element has the specified CSS styles
|
|
58
58
|
#
|
|
59
|
-
# element.
|
|
59
|
+
# element.matches_style?( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
|
|
60
60
|
#
|
|
61
61
|
# @param styles [Hash]
|
|
62
62
|
# @return [Boolean] If the styles match
|
|
63
63
|
#
|
|
64
|
+
def matches_style?(styles, **options)
|
|
65
|
+
make_predicate(options) { assert_matches_style(styles, options) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# @deprecated
|
|
70
|
+
#
|
|
64
71
|
def has_style?(styles, **options)
|
|
65
|
-
|
|
72
|
+
warn 'DEPRECATED: has_style? is deprecated, please use matches_style?'
|
|
73
|
+
matches_style?(styles, **options)
|
|
66
74
|
end
|
|
67
75
|
|
|
68
76
|
##
|
|
@@ -108,12 +116,12 @@ module Capybara
|
|
|
108
116
|
#
|
|
109
117
|
# Asserts that an element has the specified CSS styles
|
|
110
118
|
#
|
|
111
|
-
# element.
|
|
119
|
+
# element.assert_matches_style( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
|
|
112
120
|
#
|
|
113
121
|
# @param styles [Hash]
|
|
114
122
|
# @raise [Capybara::ExpectationNotMet] If the element doesn't have the specified styles
|
|
115
123
|
#
|
|
116
|
-
def
|
|
124
|
+
def assert_matches_style(styles, **options)
|
|
117
125
|
query_args = _set_query_session_options(styles, options)
|
|
118
126
|
query = Capybara::Queries::StyleQuery.new(*query_args)
|
|
119
127
|
synchronize(query.wait) do
|
|
@@ -122,6 +130,13 @@ module Capybara
|
|
|
122
130
|
true
|
|
123
131
|
end
|
|
124
132
|
|
|
133
|
+
##
|
|
134
|
+
# @deprecated
|
|
135
|
+
def assert_style(styles, **options)
|
|
136
|
+
warn 'assert_style is deprecated, please use assert_matches_style instead'
|
|
137
|
+
assert_matches_style(styles, **options)
|
|
138
|
+
end
|
|
139
|
+
|
|
125
140
|
# Asserts that all of the provided selectors are present on the given page
|
|
126
141
|
# or descendants of the current node. If options are provided, the assertion
|
|
127
142
|
# will check that each locator is present with those options as well (other than :wait).
|
data/lib/capybara/node/simple.rb
CHANGED
|
@@ -164,12 +164,12 @@ module Capybara
|
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
# @api private
|
|
167
|
-
def find_css(css)
|
|
167
|
+
def find_css(css, **_options)
|
|
168
168
|
native.css(css)
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
# @api private
|
|
172
|
-
def find_xpath(xpath)
|
|
172
|
+
def find_xpath(xpath, **_options)
|
|
173
173
|
native.xpath(xpath)
|
|
174
174
|
end
|
|
175
175
|
|
|
@@ -178,6 +178,11 @@ module Capybara
|
|
|
178
178
|
Capybara.session_options
|
|
179
179
|
end
|
|
180
180
|
|
|
181
|
+
# @api private
|
|
182
|
+
def initial_cache
|
|
183
|
+
{}
|
|
184
|
+
end
|
|
185
|
+
|
|
181
186
|
private
|
|
182
187
|
|
|
183
188
|
def option_value(option)
|
|
@@ -4,7 +4,7 @@ module Capybara
|
|
|
4
4
|
module Queries
|
|
5
5
|
class SelectorQuery < Queries::BaseQuery
|
|
6
6
|
attr_reader :expression, :selector, :locator, :options
|
|
7
|
-
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text normalize_ws match wait filter_set]
|
|
7
|
+
VALID_KEYS = COUNT_KEYS + %i[text id class style visible exact exact_text normalize_ws match wait filter_set]
|
|
8
8
|
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
|
9
9
|
|
|
10
10
|
def initialize(*args,
|
|
@@ -14,6 +14,7 @@ module Capybara
|
|
|
14
14
|
**options,
|
|
15
15
|
&filter_block)
|
|
16
16
|
@resolved_node = nil
|
|
17
|
+
@resolved_count = 0
|
|
17
18
|
@options = options.dup
|
|
18
19
|
super(@options)
|
|
19
20
|
self.session_options = session_options
|
|
@@ -50,9 +51,24 @@ module Capybara
|
|
|
50
51
|
end
|
|
51
52
|
desc << " with id #{options[:id]}" if options[:id]
|
|
52
53
|
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
|
54
|
+
desc << case options[:style]
|
|
55
|
+
when String
|
|
56
|
+
" with style attribute #{options[:style].inspect}"
|
|
57
|
+
when Regexp
|
|
58
|
+
" with style attribute matching #{options[:style].inspect}"
|
|
59
|
+
when Hash
|
|
60
|
+
" with styles #{options[:style].inspect}"
|
|
61
|
+
else ''
|
|
62
|
+
end
|
|
53
63
|
desc << selector.description(node_filters: show_for[:node], **options)
|
|
54
64
|
desc << ' that also matches the custom filter block' if @filter_block && show_for[:node]
|
|
55
65
|
desc << " within #{@resolved_node.inspect}" if describe_within?
|
|
66
|
+
if locator.is_a?(String) && locator.start_with?('#', './/', '//')
|
|
67
|
+
unless selector.raw_locator?
|
|
68
|
+
desc << "\nNote: It appears you may be passing a CSS selector or XPath expression rather than a locator. " \
|
|
69
|
+
"Please see the documentation for acceptable locator values.\n\n"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
56
72
|
desc
|
|
57
73
|
end
|
|
58
74
|
|
|
@@ -91,7 +107,9 @@ module Capybara
|
|
|
91
107
|
exact = exact? if exact.nil?
|
|
92
108
|
expr = apply_expression_filters(@expression)
|
|
93
109
|
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
|
94
|
-
filtered_expression(expr)
|
|
110
|
+
expr = filtered_expression(expr)
|
|
111
|
+
expr = "(#{expr})[#{xpath_text_conditions}]" if try_text_match_in_expression?
|
|
112
|
+
expr
|
|
95
113
|
end
|
|
96
114
|
|
|
97
115
|
def css
|
|
@@ -102,6 +120,7 @@ module Capybara
|
|
|
102
120
|
def resolve_for(node, exact = nil)
|
|
103
121
|
applied_filters.clear
|
|
104
122
|
@resolved_node = node
|
|
123
|
+
@resolved_count += 1
|
|
105
124
|
node.synchronize do
|
|
106
125
|
children = find_nodes_by_selector_format(node, exact).map(&method(:to_element))
|
|
107
126
|
Capybara::Result.new(children, self)
|
|
@@ -123,6 +142,26 @@ module Capybara
|
|
|
123
142
|
|
|
124
143
|
private
|
|
125
144
|
|
|
145
|
+
def text_fragments
|
|
146
|
+
text = (options[:text] || options[:exact_text])
|
|
147
|
+
text.is_a?(String) ? text.split : []
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def xpath_text_conditions
|
|
151
|
+
(options[:text] || options[:exact_text]).split.map { |txt| XPath.contains(txt) }.reduce(&:&)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def try_text_match_in_expression?
|
|
155
|
+
first_try? &&
|
|
156
|
+
(options[:text] || options[:exact_text]).is_a?(String) &&
|
|
157
|
+
@resolved_node&.respond_to?(:session) &&
|
|
158
|
+
@resolved_node.session.driver.wait?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def first_try?
|
|
162
|
+
@resolved_count == 1
|
|
163
|
+
end
|
|
164
|
+
|
|
126
165
|
def show_for_stage(only_applied)
|
|
127
166
|
lambda do |stage = :any|
|
|
128
167
|
!only_applied || (stage == :any ? applied_filters.any? : applied_filters.include?(stage))
|
|
@@ -141,10 +180,25 @@ module Capybara
|
|
|
141
180
|
end
|
|
142
181
|
|
|
143
182
|
def find_nodes_by_selector_format(node, exact)
|
|
183
|
+
hints = {}
|
|
184
|
+
hints[:uses_visibility] = true unless visible == :all
|
|
185
|
+
hints[:texts] = text_fragments unless selector.format == :xpath
|
|
186
|
+
hints[:styles] = options[:style] if use_default_style_filter?
|
|
187
|
+
|
|
144
188
|
if selector.format == :css
|
|
145
|
-
node.find_css
|
|
189
|
+
if node.method(:find_css).arity != 1
|
|
190
|
+
node.find_css(css, **hints)
|
|
191
|
+
else
|
|
192
|
+
node.find_css(css)
|
|
193
|
+
end
|
|
194
|
+
elsif selector.format == :xpath
|
|
195
|
+
if node.method(:find_xpath).arity != 1
|
|
196
|
+
node.find_xpath(xpath(exact), **hints)
|
|
197
|
+
else
|
|
198
|
+
node.find_xpath(xpath(exact))
|
|
199
|
+
end
|
|
146
200
|
else
|
|
147
|
-
|
|
201
|
+
raise ArgumentError, "Unknown format: #{selector.format}"
|
|
148
202
|
end
|
|
149
203
|
end
|
|
150
204
|
|
|
@@ -237,6 +291,7 @@ module Capybara
|
|
|
237
291
|
conditions = {}
|
|
238
292
|
conditions[:id] = options[:id] if use_default_id_filter?
|
|
239
293
|
conditions[:class] = options[:class] if use_default_class_filter?
|
|
294
|
+
conditions[:style] = options[:style] if use_default_style_filter? && !options[:style].is_a?(Hash)
|
|
240
295
|
builder(expr).add_attribute_conditions(conditions)
|
|
241
296
|
end
|
|
242
297
|
|
|
@@ -248,6 +303,10 @@ module Capybara
|
|
|
248
303
|
options.key?(:class) && !custom_keys.include?(:class)
|
|
249
304
|
end
|
|
250
305
|
|
|
306
|
+
def use_default_style_filter?
|
|
307
|
+
options.key?(:style) && !custom_keys.include?(:style)
|
|
308
|
+
end
|
|
309
|
+
|
|
251
310
|
def apply_expression_filters(expression)
|
|
252
311
|
unapplied_options = options.keys - valid_keys
|
|
253
312
|
expression_filters.inject(expression) do |expr, (name, ef)|
|
|
@@ -298,11 +357,12 @@ module Capybara
|
|
|
298
357
|
def matches_system_filters?(node)
|
|
299
358
|
applied_filters << :system
|
|
300
359
|
|
|
301
|
-
|
|
360
|
+
matches_visible_filter?(node) &&
|
|
361
|
+
matches_id_filter?(node) &&
|
|
302
362
|
matches_class_filter?(node) &&
|
|
363
|
+
matches_style_filter?(node) &&
|
|
303
364
|
matches_text_filter?(node) &&
|
|
304
|
-
matches_exact_text_filter?(node)
|
|
305
|
-
matches_visible_filter?(node)
|
|
365
|
+
matches_exact_text_filter?(node)
|
|
306
366
|
end
|
|
307
367
|
|
|
308
368
|
def matches_id_filter?(node)
|
|
@@ -317,6 +377,28 @@ module Capybara
|
|
|
317
377
|
node[:class] =~ options[:class]
|
|
318
378
|
end
|
|
319
379
|
|
|
380
|
+
def matches_style_filter?(node)
|
|
381
|
+
case options[:style]
|
|
382
|
+
when String, nil
|
|
383
|
+
true
|
|
384
|
+
when Regexp
|
|
385
|
+
node[:style] =~ options[:style]
|
|
386
|
+
when Hash
|
|
387
|
+
matches_style?(node, options[:style])
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def matches_style?(node, styles)
|
|
392
|
+
@actual_styles = node.initial_cache[:style] || node.style(*styles.keys)
|
|
393
|
+
styles.all? do |style, value|
|
|
394
|
+
if value.is_a? Regexp
|
|
395
|
+
@actual_styles[style.to_s] =~ value
|
|
396
|
+
else
|
|
397
|
+
@actual_styles[style.to_s] == value
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
320
402
|
def matches_text_filter?(node)
|
|
321
403
|
value = options[:text]
|
|
322
404
|
return true unless value
|
|
@@ -334,8 +416,10 @@ module Capybara
|
|
|
334
416
|
|
|
335
417
|
def matches_visible_filter?(node)
|
|
336
418
|
case visible
|
|
337
|
-
when :visible then
|
|
338
|
-
|
|
419
|
+
when :visible then
|
|
420
|
+
node.initial_cache[:visible] || (node.initial_cache[:visible].nil? && node.visible?)
|
|
421
|
+
when :hidden then
|
|
422
|
+
(node.initial_cache[:visible] == false) || (node.initial_cache[:visbile].nil? && !node.visible?)
|
|
339
423
|
else true
|
|
340
424
|
end
|
|
341
425
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'capybara/rspec/matchers/base'
|
|
4
|
+
|
|
5
|
+
module Capybara
|
|
6
|
+
module RSpecMatchers
|
|
7
|
+
module Matchers
|
|
8
|
+
class MatchStyle < WrappedElementMatcher
|
|
9
|
+
def element_matches?(el)
|
|
10
|
+
el.assert_matches_style(*@args)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def does_not_match?(_actual)
|
|
14
|
+
raise ArgumentError, 'The match_style matcher does not support use with not_to/should_not'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def description
|
|
18
|
+
'match style'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Capybara
|
|
26
|
+
module RSpecMatchers
|
|
27
|
+
module Matchers
|
|
28
|
+
##
|
|
29
|
+
# @deprecated
|
|
30
|
+
class HaveStyle < MatchStyle
|
|
31
|
+
def initialize(*args, &filter_block)
|
|
32
|
+
warn 'HaveStyle matcher is deprecated, please use the MatchStyle matcher instead'
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|