capybara 3.2.1 → 3.3.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 +22 -1
- data/README.md +1 -1
- data/lib/capybara.rb +2 -0
- data/lib/capybara/driver/base.rb +5 -1
- data/lib/capybara/driver/node.rb +4 -0
- data/lib/capybara/helpers.rb +25 -0
- data/lib/capybara/minitest.rb +7 -1
- data/lib/capybara/minitest/spec.rb +8 -1
- data/lib/capybara/node/base.rb +3 -3
- data/lib/capybara/node/element.rb +52 -0
- data/lib/capybara/node/matchers.rb +33 -0
- data/lib/capybara/queries/selector_query.rb +4 -4
- data/lib/capybara/queries/style_query.rb +41 -0
- data/lib/capybara/rack_test/browser.rb +7 -1
- data/lib/capybara/rack_test/node.rb +4 -0
- data/lib/capybara/rspec/compound.rb +67 -65
- data/lib/capybara/rspec/features.rb +2 -4
- data/lib/capybara/rspec/matchers.rb +30 -10
- data/lib/capybara/selector.rb +9 -0
- data/lib/capybara/selector/css.rb +74 -1
- data/lib/capybara/selector/filters/base.rb +2 -1
- data/lib/capybara/selector/filters/expression_filter.rb +2 -1
- data/lib/capybara/selector/selector.rb +1 -1
- data/lib/capybara/selenium/driver.rb +34 -43
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +35 -0
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +31 -0
- data/lib/capybara/selenium/node.rb +17 -20
- data/lib/capybara/selenium/nodes/marionette_node.rb +31 -0
- data/lib/capybara/server.rb +8 -29
- data/lib/capybara/server/checker.rb +38 -0
- data/lib/capybara/spec/public/test.js +5 -0
- data/lib/capybara/spec/session/all_spec.rb +4 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
- data/lib/capybara/spec/session/click_button_spec.rb +10 -0
- data/lib/capybara/spec/session/click_link_spec.rb +11 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +2 -0
- data/lib/capybara/spec/session/find_link_spec.rb +18 -0
- data/lib/capybara/spec/session/find_spec.rb +1 -0
- data/lib/capybara/spec/session/first_spec.rb +1 -0
- data/lib/capybara/spec/session/has_css_spec.rb +0 -6
- data/lib/capybara/spec/session/has_style_spec.rb +25 -0
- data/lib/capybara/spec/session/node_spec.rb +34 -0
- data/lib/capybara/spec/session/save_page_spec.rb +4 -1
- data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -1
- data/lib/capybara/spec/session/text_spec.rb +1 -0
- data/lib/capybara/spec/session/title_spec.rb +1 -0
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -0
- data/lib/capybara/spec/session/window/window_spec.rb +20 -0
- data/lib/capybara/spec/session/window/windows_spec.rb +1 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +1 -0
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +3 -1
- data/lib/capybara/spec/test_app.rb +18 -0
- data/lib/capybara/spec/views/form.erb +8 -0
- data/lib/capybara/spec/views/tables.erb +1 -1
- data/lib/capybara/spec/views/with_html.erb +9 -2
- data/lib/capybara/spec/views/with_js.erb +4 -0
- data/lib/capybara/spec/views/with_namespace.erb +20 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +11 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +1 -1
- data/spec/minitest_spec.rb +7 -1
- data/spec/minitest_spec_spec.rb +8 -1
- data/spec/rack_test_spec.rb +10 -0
- data/spec/rspec/shared_spec_matchers.rb +2 -0
- data/spec/selenium_spec_chrome.rb +28 -0
- data/spec/selenium_spec_chrome_remote.rb +2 -2
- data/spec/selenium_spec_marionette.rb +21 -1
- data/spec/server_spec.rb +0 -1
- data/spec/shared_selenium_session.rb +16 -1
- metadata +18 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec1af73714c227e46b54e14d716a63f2cc75011d2cb8f0fde62eecb8d5d07785
|
4
|
+
data.tar.gz: 907fecc783f2a9b23529e10b0e4775dc0f1015ffc5163157523772d192456e5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5c6f7cdf74cb231ee795c299f7ee28688dc8ecfd371b97c1744e77a823209e5270626cdf36e8c171fe1018394d1f1176b15e0b61e88e18f4c60ff402d1aad88
|
7
|
+
data.tar.gz: 29fcba8784b579ff159a00df91ddf83aa0cdc14fb6061535943933aa89796848068b8dea1da723cdaeda9ec3a3c81c8ce7fb7857f052b3764ffe7cbcea790d93
|
data/History.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
# Version 3.3.0
|
2
|
+
Release date: 2018-06-25
|
3
|
+
|
4
|
+
### Added
|
5
|
+
|
6
|
+
* RackTest driver now handles 307/308 redirects
|
7
|
+
* `execute_async_script` can now be called on elements to run the JS in the context of the element
|
8
|
+
* `:download` filter option on `:link' selector
|
9
|
+
* `Window#fullscreen`
|
10
|
+
* `Element#style` and associated matchers
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
* Minimum "supported" `selenium-webdriver` is raised to 3.5.0 (but you really should be using newer than that)
|
15
|
+
|
16
|
+
### Fixes
|
17
|
+
|
18
|
+
* Selenium driver with Firefox workaround for clicking on table row - https://github.com/mozilla/geckodriver/issues/1228
|
19
|
+
* :class and :id filters applied to CSS based selectors now correctly handle the CSS comma
|
20
|
+
* Selenium driver handles namespaces when generating an elements `#path` - Issue #2048
|
21
|
+
|
1
22
|
# Version 3.2.1
|
2
23
|
Release date: 2018-06-04
|
3
24
|
|
@@ -17,7 +38,7 @@ Release date: 2018-06-01
|
|
17
38
|
### Added
|
18
39
|
|
19
40
|
* New global configuration `default_set_options` used in `Capybara::Node::Element#set` as default `options` hash [Champier Cyril]
|
20
|
-
* `
|
41
|
+
* `execute_script` and `evaluate_script` can now be called on elements to run the JS in the context of the element [Thomas Walpole]
|
21
42
|
* Filters in custom selectors now support a `matcher` Regexp to handle multiple filter options [Thomas Walpole]
|
22
43
|
* `:element` selector type which will match on any attribute (other than the reserved names) passed as a filter option [Thomas Walpole]
|
23
44
|
* `:class` filter option now supports preceding class names with `!` to indicate not having that class [Thomas Walpole]
|
data/README.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/teamcapybara/capybara)
|
4
4
|
[](https://ci.appveyor.com/api/projects/github/teamcapybara/capybara)
|
5
|
-
[](https://gemnasium.com/teamcapybara/capybara)
|
6
5
|
[](https://codeclimate.com/github/teamcapybara/capybara)
|
7
6
|
[](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
|
+
[](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
|
8
8
|
|
9
9
|
**Note** You are viewing the README for the development version of Capybara. If you are using the current release version
|
10
10
|
you can find the README at https://github.com/teamcapybara/capybara/blob/3.2_stable/README.md
|
data/lib/capybara.rb
CHANGED
@@ -406,6 +406,7 @@ module Capybara
|
|
406
406
|
require 'capybara/queries/match_query'
|
407
407
|
require 'capybara/queries/ancestor_query'
|
408
408
|
require 'capybara/queries/sibling_query'
|
409
|
+
require 'capybara/queries/style_query'
|
409
410
|
|
410
411
|
require 'capybara/node/finders'
|
411
412
|
require 'capybara/node/matchers'
|
@@ -426,6 +427,7 @@ module Capybara
|
|
426
427
|
require 'capybara/rack_test/css_handlers.rb'
|
427
428
|
|
428
429
|
require 'capybara/selenium/node'
|
430
|
+
require 'capybara/selenium/nodes/marionette_node'
|
429
431
|
require 'capybara/selenium/driver'
|
430
432
|
end
|
431
433
|
|
data/lib/capybara/driver/base.rb
CHANGED
@@ -90,7 +90,11 @@ class Capybara::Driver::Base
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def maximize_window(handle)
|
93
|
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#
|
93
|
+
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#maximize_window'
|
94
|
+
end
|
95
|
+
|
96
|
+
def fullscreen_window(handle)
|
97
|
+
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#fullscreen_window'
|
94
98
|
end
|
95
99
|
|
96
100
|
def close_window(handle)
|
data/lib/capybara/driver/node.rb
CHANGED
@@ -26,6 +26,10 @@ module Capybara
|
|
26
26
|
raise NotImplementedError
|
27
27
|
end
|
28
28
|
|
29
|
+
def style(styles)
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
29
33
|
# @param value String or Array. Array is only allowed if node has 'multiple' attribute
|
30
34
|
# @param options [Hash{}] Driver specific options for how to set a value on a node
|
31
35
|
def set(value, **options)
|
data/lib/capybara/helpers.rb
CHANGED
@@ -74,5 +74,30 @@ module Capybara
|
|
74
74
|
else
|
75
75
|
def monotonic_time; Time.now.to_f; end
|
76
76
|
end
|
77
|
+
|
78
|
+
def timer(expire_in:)
|
79
|
+
Timer.new(expire_in)
|
80
|
+
end
|
81
|
+
|
82
|
+
class Timer
|
83
|
+
def initialize(expire_in)
|
84
|
+
@start = current
|
85
|
+
@expire_in = expire_in
|
86
|
+
end
|
87
|
+
|
88
|
+
def expired?
|
89
|
+
current - @start >= @expire_in
|
90
|
+
end
|
91
|
+
|
92
|
+
def stalled?
|
93
|
+
@start == current
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def current
|
99
|
+
Capybara::Helpers.monotonic_time
|
100
|
+
end
|
101
|
+
end
|
77
102
|
end
|
78
103
|
end
|
data/lib/capybara/minitest.rb
CHANGED
@@ -81,9 +81,15 @@ module Capybara
|
|
81
81
|
# @!method assert_xpath
|
82
82
|
# see {Capybara::Node::Matchers#assert_not_matches_selector}
|
83
83
|
|
84
|
+
## Assert element has the provided CSS styles
|
85
|
+
#
|
86
|
+
# @!method assert_style
|
87
|
+
# see {Capybara::Node::Matchers#assert_style}
|
88
|
+
|
84
89
|
%w[assert_selector assert_no_selector
|
85
90
|
assert_all_of_selectors assert_none_of_selectors
|
86
|
-
assert_matches_selector assert_not_matches_selector
|
91
|
+
assert_matches_selector assert_not_matches_selector
|
92
|
+
assert_style].each do |assertion_name|
|
87
93
|
class_eval <<-ASSERTION, __FILE__, __LINE__ + 1
|
88
94
|
def #{assertion_name} *args, &optional_filter_block
|
89
95
|
self.assertions +=1
|
@@ -15,7 +15,8 @@ module Capybara
|
|
15
15
|
[%W[assert_#{assertion} must_have_#{assertion}],
|
16
16
|
%W[refute_#{assertion} wont_have_#{assertion}]]
|
17
17
|
end + [%w[assert_all_of_selectors must_have_all_of_selectors],
|
18
|
-
%w[assert_none_of_selectors must_have_none_of_selectors]
|
18
|
+
%w[assert_none_of_selectors must_have_none_of_selectors],
|
19
|
+
%w[assert_style must_have_style]] +
|
19
20
|
%w[selector xpath css].flat_map do |assertion|
|
20
21
|
[%W[assert_matches_#{assertion} must_match_#{assertion}],
|
21
22
|
%W[refute_matches_#{assertion} wont_match_#{assertion}]]
|
@@ -163,6 +164,12 @@ module Capybara
|
|
163
164
|
#
|
164
165
|
# @!method wont_have_current_path
|
165
166
|
# see {Capybara::SessionMatchers#assert_no_current_path}
|
167
|
+
|
168
|
+
##
|
169
|
+
# Expectation that element has style
|
170
|
+
#
|
171
|
+
# @!method must_have_style
|
172
|
+
# see {Capybara::SessionMatchers#assert_style}
|
166
173
|
end
|
167
174
|
end
|
168
175
|
end
|
data/lib/capybara/node/base.rb
CHANGED
@@ -78,15 +78,15 @@ module Capybara
|
|
78
78
|
yield
|
79
79
|
else
|
80
80
|
session.synchronized = true
|
81
|
-
|
81
|
+
timer = Capybara::Helpers.timer(expire_in: seconds)
|
82
82
|
begin
|
83
83
|
yield
|
84
84
|
rescue StandardError => e
|
85
85
|
session.raise_server_error!
|
86
86
|
raise e unless driver.wait? && catch_error?(e, errors)
|
87
|
-
raise e if
|
87
|
+
raise e if timer.expired?
|
88
88
|
sleep(0.05)
|
89
|
-
raise Capybara::FrozenInTime, "Time appears to be frozen. Capybara does not work with libraries which freeze time, consider using time travelling instead" if
|
89
|
+
raise Capybara::FrozenInTime, "Time appears to be frozen. Capybara does not work with libraries which freeze time, consider using time travelling instead" if timer.stalled?
|
90
90
|
reload if session_options.automatic_reload
|
91
91
|
retry
|
92
92
|
ensure
|
@@ -71,6 +71,29 @@ module Capybara
|
|
71
71
|
synchronize { base[attribute] }
|
72
72
|
end
|
73
73
|
|
74
|
+
##
|
75
|
+
#
|
76
|
+
# Retrieve the given CSS styles
|
77
|
+
#
|
78
|
+
# element.style('color', 'font-size') # => Computed values of CSS 'color' and 'font-size' styles
|
79
|
+
#
|
80
|
+
# @param [String] Names of the desired CSS properties
|
81
|
+
# @return [Hash] Hash of the CSS property names to computed values
|
82
|
+
#
|
83
|
+
def style(*styles)
|
84
|
+
styles = styles.flatten.map(&:to_s)
|
85
|
+
raise ArgumentError, "You must specify at least one CSS style" if styles.empty?
|
86
|
+
begin
|
87
|
+
synchronize { base.style(styles) }
|
88
|
+
rescue NotImplementedError => e
|
89
|
+
begin
|
90
|
+
evaluate_script(STYLE_SCRIPT, *styles)
|
91
|
+
rescue Capybara::NotSupportedByDriverError
|
92
|
+
raise e
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
74
97
|
##
|
75
98
|
#
|
76
99
|
# @return [String] The value of the form element
|
@@ -381,6 +404,23 @@ module Capybara
|
|
381
404
|
JS
|
382
405
|
end
|
383
406
|
|
407
|
+
##
|
408
|
+
#
|
409
|
+
# Evaluate the given JavaScript in the context of the element and obtain the result from a
|
410
|
+
# callback function which will be passed as the last argument to the script. `this` in the
|
411
|
+
# script will refer to the element this is called on
|
412
|
+
#
|
413
|
+
# @param [String] script A string of JavaScript to evaluate
|
414
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
415
|
+
#
|
416
|
+
def evaluate_async_script(script, *args)
|
417
|
+
session.evaluate_async_script(<<~JS, self, *args)
|
418
|
+
(function (){
|
419
|
+
#{script}
|
420
|
+
}).apply(arguments[0], Array.prototype.slice.call(arguments,1));
|
421
|
+
JS
|
422
|
+
end
|
423
|
+
|
384
424
|
def reload
|
385
425
|
if @allow_reload
|
386
426
|
begin
|
@@ -402,6 +442,18 @@ module Capybara
|
|
402
442
|
|
403
443
|
%(Obsolete #<Capybara::Node::Element>)
|
404
444
|
end
|
445
|
+
|
446
|
+
STYLE_SCRIPT = <<~JS
|
447
|
+
(function(){
|
448
|
+
var s = window.getComputedStyle(this);
|
449
|
+
var result = {};
|
450
|
+
for (var i = arguments.length; i--; ) {
|
451
|
+
var property_name = arguments[i];
|
452
|
+
result[property_name] = s.getPropertyValue(property_name);
|
453
|
+
}
|
454
|
+
return result;
|
455
|
+
}).apply(this, arguments)
|
456
|
+
JS
|
405
457
|
end
|
406
458
|
end
|
407
459
|
end
|
@@ -56,6 +56,21 @@ module Capybara
|
|
56
56
|
false
|
57
57
|
end
|
58
58
|
|
59
|
+
##
|
60
|
+
#
|
61
|
+
# Checks if a an element has the specified CSS styles
|
62
|
+
#
|
63
|
+
# element.has_style?( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
|
64
|
+
#
|
65
|
+
# @param styles [Hash]
|
66
|
+
# @return [Boolean] If the styles match
|
67
|
+
#
|
68
|
+
def has_style?(styles, **options)
|
69
|
+
assert_style(styles, **options)
|
70
|
+
rescue Capybara::ExpectationNotMet
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
59
74
|
##
|
60
75
|
#
|
61
76
|
# Asserts that a given selector is on the page or a descendant of the current node.
|
@@ -97,6 +112,24 @@ module Capybara
|
|
97
112
|
end
|
98
113
|
end
|
99
114
|
|
115
|
+
##
|
116
|
+
#
|
117
|
+
# Asserts that an element has the specified CSS styles
|
118
|
+
#
|
119
|
+
# element.assert_style( 'color' => 'rgb(0,0,255)', 'font-size' => /px/ )
|
120
|
+
#
|
121
|
+
# @param styles [Hash]
|
122
|
+
# @raise [Capybara::ExpectationNotMet] If the element doesn't have the specified styles
|
123
|
+
#
|
124
|
+
def assert_style(styles, **options)
|
125
|
+
query_args = _set_query_session_options(styles, options)
|
126
|
+
query = Capybara::Queries::StyleQuery.new(*query_args)
|
127
|
+
synchronize(query.wait) do
|
128
|
+
raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
|
129
|
+
end
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
100
133
|
# Asserts that all of the provided selectors are present on the given page
|
101
134
|
# or descendants of the current node. If options are provided, the assertion
|
102
135
|
# will check that each locator is present with those options as well (other than :wait).
|
@@ -225,14 +225,14 @@ module Capybara
|
|
225
225
|
raise ArgumentError, "XPath expressions are not supported for the :class filter with CSS based selectors"
|
226
226
|
end
|
227
227
|
|
228
|
-
if
|
229
|
-
|
230
|
-
|
231
|
-
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if process_id
|
228
|
+
if process_id || process_class
|
229
|
+
expr = ::Capybara::Selector::CSS.split(expr).map do |sel|
|
230
|
+
sel += "##{::Capybara::Selector::CSS.escape(options[:id])}" if process_id
|
232
231
|
sel += css_from_classes(Array(options[:class])) if process_class
|
233
232
|
sel
|
234
233
|
end.join(", ")
|
235
234
|
end
|
235
|
+
|
236
236
|
expr
|
237
237
|
end
|
238
238
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara
|
4
|
+
# @api private
|
5
|
+
module Queries
|
6
|
+
class StyleQuery < BaseQuery
|
7
|
+
def initialize(expected_styles, session_options:, **options)
|
8
|
+
@expected_styles = expected_styles.each_with_object({}) { |(style, value), str_keys| str_keys[style.to_s] = value }
|
9
|
+
@options = options
|
10
|
+
@actual_styles = {}
|
11
|
+
super(@options)
|
12
|
+
self.session_options = session_options
|
13
|
+
|
14
|
+
assert_valid_keys
|
15
|
+
end
|
16
|
+
|
17
|
+
def resolves_for?(node)
|
18
|
+
@node = node
|
19
|
+
@actual_styles = node.style(*@expected_styles.keys)
|
20
|
+
@expected_styles.all? do |style, value|
|
21
|
+
if value.is_a? Regexp
|
22
|
+
@actual_styles[style] =~ value
|
23
|
+
else
|
24
|
+
@actual_styles[style] == value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_message
|
30
|
+
+"Expected node to have styles #{@expected_styles.inspect}. " \
|
31
|
+
"Actual styles were #{@actual_styles.inspect}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def valid_keys
|
37
|
+
%i[wait]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -44,7 +44,13 @@ class Capybara::RackTest::Browser
|
|
44
44
|
return unless driver.follow_redirects?
|
45
45
|
|
46
46
|
driver.redirect_limit.times do
|
47
|
-
|
47
|
+
if last_response.redirect?
|
48
|
+
if [307, 308].include? last_response.status
|
49
|
+
process(last_request.request_method, last_response["Location"], last_request.params, env)
|
50
|
+
else
|
51
|
+
process(:get, last_response["Location"], {}, env)
|
52
|
+
end
|
53
|
+
end
|
48
54
|
end
|
49
55
|
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
50
56
|
end
|
@@ -1,89 +1,91 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module
|
5
|
-
module
|
6
|
-
|
3
|
+
if defined?(::RSpec::Expectations::Version)
|
4
|
+
module Capybara
|
5
|
+
module RSpecMatchers
|
6
|
+
module Compound
|
7
|
+
include ::RSpec::Matchers::Composable
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def and_then(matcher)
|
13
|
-
::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
|
14
|
-
end
|
15
|
-
|
16
|
-
def or(matcher)
|
17
|
-
Or.new(self, matcher)
|
18
|
-
end
|
9
|
+
def and(matcher)
|
10
|
+
And.new(self, matcher)
|
11
|
+
end
|
19
12
|
|
20
|
-
|
21
|
-
|
22
|
-
@actual = actual
|
23
|
-
@match_results = Hash.new { |h, matcher| h[matcher] = matcher.matches?(@actual) }
|
13
|
+
def and_then(matcher)
|
14
|
+
::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
|
24
15
|
end
|
25
16
|
|
26
|
-
def
|
27
|
-
|
17
|
+
def or(matcher)
|
18
|
+
Or.new(self, matcher)
|
28
19
|
end
|
29
20
|
|
30
|
-
|
31
|
-
|
21
|
+
class CapybaraEvaluator
|
22
|
+
def initialize(actual)
|
23
|
+
@actual = actual
|
24
|
+
@match_results = Hash.new { |h, matcher| h[matcher] = matcher.matches?(@actual) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def matcher_matches?(matcher)
|
28
|
+
@match_results[matcher]
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@match_results.clear
|
33
|
+
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
36
|
+
class And < ::RSpec::Matchers::BuiltIn::Compound::And
|
37
|
+
private
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def match(_expected, actual)
|
40
|
+
@evaluator = CapybaraEvaluator.new(actual)
|
41
|
+
syncer = sync_element(actual)
|
42
|
+
begin
|
43
|
+
syncer.synchronize do
|
44
|
+
@evaluator.reset
|
45
|
+
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].all?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
rescue StandardError
|
49
|
+
false
|
46
50
|
end
|
47
|
-
rescue StandardError
|
48
|
-
false
|
49
51
|
end
|
50
|
-
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
def sync_element(el)
|
54
|
+
if el.respond_to? :synchronize
|
55
|
+
el
|
56
|
+
elsif el.respond_to? :current_scope
|
57
|
+
el.current_scope
|
58
|
+
else
|
59
|
+
Capybara.string(el)
|
60
|
+
end
|
59
61
|
end
|
60
62
|
end
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
-
|
64
|
+
class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
|
65
|
+
private
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
def match(_expected, actual)
|
68
|
+
@evaluator = CapybaraEvaluator.new(actual)
|
69
|
+
syncer = sync_element(actual)
|
70
|
+
begin
|
71
|
+
syncer.synchronize do
|
72
|
+
@evaluator.reset
|
73
|
+
raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].any?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
rescue StandardError
|
77
|
+
false
|
74
78
|
end
|
75
|
-
rescue StandardError
|
76
|
-
false
|
77
79
|
end
|
78
|
-
end
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
81
|
+
def sync_element(el)
|
82
|
+
if el.respond_to? :synchronize
|
83
|
+
el
|
84
|
+
elsif el.respond_to? :current_scope
|
85
|
+
el.current_scope
|
86
|
+
else
|
87
|
+
Capybara.string(el)
|
88
|
+
end
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|