capybara 3.2.1 → 3.3.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 +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
|
[![Build Status](https://secure.travis-ci.org/teamcapybara/capybara.svg)](https://travis-ci.org/teamcapybara/capybara)
|
4
4
|
[![Build Status](https://ci.appveyor.com/api/projects/status/github/teamcapybara/capybara?svg=true)](https://ci.appveyor.com/api/projects/github/teamcapybara/capybara)
|
5
|
-
[![Dependency Status](https://gemnasium.com/teamcapybara/capybara.svg)](https://gemnasium.com/teamcapybara/capybara)
|
6
5
|
[![Code Climate](https://codeclimate.com/github/teamcapybara/capybara.svg)](https://codeclimate.com/github/teamcapybara/capybara)
|
7
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
|
+
[![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
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
|