capybara 2.14.4 → 2.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +17 -0
  3. data/README.md +14 -22
  4. data/lib/capybara.rb +16 -4
  5. data/lib/capybara/config.rb +12 -2
  6. data/lib/capybara/driver/base.rb +4 -0
  7. data/lib/capybara/node/finders.rb +79 -16
  8. data/lib/capybara/queries/ancestor_query.rb +25 -0
  9. data/lib/capybara/queries/selector_query.rb +4 -1
  10. data/lib/capybara/queries/sibling_query.rb +25 -0
  11. data/lib/capybara/rack_test/browser.rb +5 -0
  12. data/lib/capybara/rack_test/driver.rb +5 -1
  13. data/lib/capybara/rack_test/form.rb +1 -3
  14. data/lib/capybara/rack_test/node.rb +1 -1
  15. data/lib/capybara/rspec/compound.rb +95 -0
  16. data/lib/capybara/rspec/matchers.rb +4 -1
  17. data/lib/capybara/selector.rb +1 -0
  18. data/lib/capybara/selector/filter.rb +13 -41
  19. data/lib/capybara/selector/filter_set.rb +12 -5
  20. data/lib/capybara/selector/filters/base.rb +33 -0
  21. data/lib/capybara/selector/filters/expression_filter.rb +40 -0
  22. data/lib/capybara/selector/filters/node_filter.rb +27 -0
  23. data/lib/capybara/selector/selector.rb +4 -4
  24. data/lib/capybara/selenium/driver.rb +12 -2
  25. data/lib/capybara/selenium/node.rb +70 -55
  26. data/lib/capybara/session.rb +65 -41
  27. data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
  28. data/lib/capybara/spec/session/attach_file_spec.rb +1 -1
  29. data/lib/capybara/spec/session/check_spec.rb +4 -4
  30. data/lib/capybara/spec/session/choose_spec.rb +2 -2
  31. data/lib/capybara/spec/session/click_button_spec.rb +1 -1
  32. data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
  33. data/lib/capybara/spec/session/click_link_spec.rb +1 -1
  34. data/lib/capybara/spec/session/fill_in_spec.rb +2 -2
  35. data/lib/capybara/spec/session/find_spec.rb +1 -1
  36. data/lib/capybara/spec/session/refresh_spec.rb +28 -0
  37. data/lib/capybara/spec/session/select_spec.rb +2 -2
  38. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  39. data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
  40. data/lib/capybara/spec/session/unselect_spec.rb +2 -2
  41. data/lib/capybara/spec/session/window/become_closed_spec.rb +3 -3
  42. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
  43. data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
  44. data/lib/capybara/spec/test_app.rb +3 -1
  45. data/lib/capybara/spec/views/with_html.erb +27 -1
  46. data/lib/capybara/spec/views/with_windows.erb +4 -0
  47. data/lib/capybara/version.rb +1 -1
  48. data/spec/capybara_spec.rb +9 -1
  49. data/spec/minitest_spec_spec.rb +1 -1
  50. data/spec/rspec/shared_spec_matchers.rb +146 -42
  51. data/spec/selenium_spec_chrome.rb +12 -16
  52. data/spec/selenium_spec_marionette.rb +2 -0
  53. data/spec/shared_selenium_session.rb +2 -0
  54. metadata +14 -6
  55. data/lib/capybara/selector/expression_filter.rb +0 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 987f25342ad56a2981f48c4cee5894c01601450e
4
- data.tar.gz: c9c97e994be43ebde7ec94cd8cebcfcec74c2ad3
3
+ metadata.gz: 4e019a48b2f87598c4c62ba40e9fc12291b8f538
4
+ data.tar.gz: 2625bd193da4cdab59284069d1b5b2cdd8430b89
5
5
  SHA512:
6
- metadata.gz: 217162eb0ad144e6617666bc998f3f2dda3ccbd93b57f787762314f58203a53dcad4e2d78f9f760807cea15b597c3b6f3aa7533da4fe841af2aab73b211e222d
7
- data.tar.gz: 59aef00f83df73f98197550976838d7b9e8690d89756a070497c3429a705b78d82b5807a89a24eebae0513277cf38bf23cf155bb32e1c6e4da17fa3b58709dee
6
+ metadata.gz: 682fdf80248b4da8fefe635e74f1ad57ead7559198853f67500fa9a8487f3464904d3ad622a38f0d4f73dc60e40db04cab8d965c0e3bc6a8a6467f115241d418
7
+ data.tar.gz: 80945a70a931293fd4929c756b39b340d5e78c20a5f547eab84264505fdbe5c9fb3461c08b9e8fc3839fcfff8e741f40b9272ae285578a33fea47f28179b86a9
data/History.md CHANGED
@@ -1,3 +1,20 @@
1
+ # Version 2.15.0
2
+
3
+ Release data: unreleased
4
+
5
+ ### Added
6
+
7
+ * `sibling` and `ancestor` finders added [Thomas Walpole]
8
+ * Added ability to pass options to registered servers when setting
9
+ * Added basic built-in driver registrations `:selenium_chrome` and `:selenium_chrome_headless` [Thomas Walpole]
10
+ * Add `and_then` to Capybara RSpec matchers which behaves like the previous `and` compounder. [Thomas Walpole]
11
+ * Compound RSpec expectations with Capybara matchers now run both matchers inside a retry loop rather
12
+ than waiting for one to pass/fail before checking the second. Will make `#or` more performant and confirm
13
+ both conditions are true "simultaneously" for `and`. [Thomas Walpole]
14
+ If you still want the
15
+ * Add `Session#refresh` [Thomas Walpole]
16
+ * Loosened restrictions on where `Session#within_window` can be called from [Thomas Walpole]
17
+ * Switched from `mime-types` dependency to `mini_mime` [Jason Frey]
1
18
  # Version 2.14.4
2
19
 
3
20
  Release date: 2017-06-27
data/README.md CHANGED
@@ -13,22 +13,6 @@ through an external gem.
13
13
  **Need help?** Ask on the mailing list (please do not open an issue on
14
14
  GitHub): http://groups.google.com/group/ruby-capybara
15
15
 
16
- **Note: Firefox 48+** If you're using Firefox with selenium-webdriver and want full functionality stay on either Firefox [45.0esr](https://ftp.mozilla.org/pub/firefox/releases/45.0esr/) or [47.0.1](https://ftp.mozilla.org/pub/firefox/releases/47.0.1/).
17
- If using selenium-webdriver 3.0+ this will require configuring your driver with the `marionette: false` option as shown below
18
-
19
- ```ruby
20
- Capybara.register_driver :selenium do |app|
21
- Capybara::Selenium::Driver.new(
22
- app,
23
- browser: :firefox,
24
- desired_capabilities: Selenium::WebDriver::Remote::Capabilities.firefox(marionette: false)
25
- )
26
- end
27
- ```
28
-
29
- Using Firefox 48+ requires geckodriver and selenium-webdriver v3, the combo of which currently has multiple issues and is feature incomplete.
30
- You can read more about the missing features [here](https://github.com/teamcapybara/capybara/issues/1710).
31
-
32
16
  ## Table of contents
33
17
 
34
18
  - [Key benefits](#key-benefits)
@@ -88,7 +72,7 @@ Capybara requires Ruby 1.9.3 or later. To install, add this line to your
88
72
  gem 'capybara'
89
73
  ```
90
74
 
91
- **Note:** If using Ruby < 2.0 you will also need to limit the version of mime-types to < 3.0 and the version of rack to < 2.0
75
+ **Note:** If using Ruby < 2.0 you will also need to limit the version of rack to < 2.0
92
76
 
93
77
  If the application that you are testing is a Rails app, add this line to your test helper file:
94
78
 
@@ -106,7 +90,13 @@ Capybara.app = MyRackApp
106
90
  ```
107
91
 
108
92
  If you need to test JavaScript, or if your app interacts with (or is located at)
109
- a remote URL, you'll need to [use a different driver](#drivers).
93
+ a remote URL, you'll need to [use a different driver](#drivers). If using Rails 5.0+, but not using the Rails system tests from 5.1, you'll probably also
94
+ want to swap the "server" used to launch your app to Puma in order to match Rails defaults.
95
+
96
+ ```ruby
97
+ Capybara.server = :puma # Until your setup is working
98
+ Capybara.server = :puma, { Silent: true } # To clean up your test output
99
+ ```
110
100
 
111
101
  ## <a name="using-capybara-with-cucumber"></a>Using Capybara with Cucumber
112
102
 
@@ -140,8 +130,7 @@ Scenario: do something Ajaxy
140
130
  ...
141
131
  ```
142
132
 
143
- There are also explicit `@selenium` and `@rack_test`
144
- tags set up for you.
133
+ There are also explicit tags for each registered driver set up for you (`@selenium`, `@rack_test`, etc).
145
134
 
146
135
  ## <a name="using-capybara-with-rspec"></a>Using Capybara with RSpec
147
136
 
@@ -343,7 +332,7 @@ these limitations, you can set up a different default driver for your features.
343
332
  For example if you'd prefer to run everything in Selenium, you could do:
344
333
 
345
334
  ```ruby
346
- Capybara.default_driver = :selenium
335
+ Capybara.default_driver = :selenium # :selenium_chrome and :selenium_chrome_headless are also registered
347
336
  ```
348
337
 
349
338
  However, if you are using RSpec or Cucumber, you may instead want to consider
@@ -392,7 +381,7 @@ See the section on adding and configuring drivers.
392
381
 
393
382
  ### <a name="selenium"></a>Selenium
394
383
 
395
- At the moment, Capybara supports [Selenium 2.0
384
+ At the moment, Capybara supports [Selenium 2.0+
396
385
  (Webdriver)](http://seleniumhq.org/docs/01_introducing_selenium.html#selenium-2-aka-selenium-webdriver),
397
386
  *not* Selenium RC. In order to use Selenium, you'll need to install the
398
387
  `selenium-webdriver` gem, and add it to your Gemfile if you're using bundler.
@@ -747,6 +736,9 @@ Capybara 1.x, set `Capybara.match` to `:prefer_exact`.
747
736
 
748
737
  ## <a name="transactions-and-database-setup"></a>Transactions and database setup
749
738
 
739
+ **Note:** Rails 5.1+ now "safely" shares the database connection between the app and test threads. Therefore,
740
+ if using Rails 5.1+ you SHOULD be able to ignore this section.
741
+
750
742
  Some Capybara drivers need to run against an actual HTTP server. Capybara takes
751
743
  care of this and starts one for you in the same process as your test, but on
752
744
  another thread. Selenium is one of those drivers, whereas RackTest is not.
@@ -413,6 +413,8 @@ module Capybara
413
413
  require 'capybara/queries/title_query'
414
414
  require 'capybara/queries/current_path_query'
415
415
  require 'capybara/queries/match_query'
416
+ require 'capybara/queries/ancestor_query'
417
+ require 'capybara/queries/sibling_query'
416
418
  require 'capybara/query'
417
419
 
418
420
  require 'capybara/node/finders'
@@ -441,14 +443,14 @@ Capybara.register_server :default do |app, port, _host|
441
443
  Capybara.run_default_server(app, port)
442
444
  end
443
445
 
444
- Capybara.register_server :webrick do |app, port, host|
446
+ Capybara.register_server :webrick do |app, port, host, options={}|
445
447
  require 'rack/handler/webrick'
446
- Rack::Handler::WEBrick.run(app, Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0))
448
+ Rack::Handler::WEBrick.run(app, {Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0)}.merge(options))
447
449
  end
448
450
 
449
- Capybara.register_server :puma do |app, port, host|
451
+ Capybara.register_server :puma do |app, port, host, options={}|
450
452
  require 'rack/handler/puma'
451
- Rack::Handler::Puma.run(app, Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false)
453
+ Rack::Handler::Puma.run(app, {Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false}.merge(options))
452
454
  end
453
455
 
454
456
  Capybara.configure do |config|
@@ -480,3 +482,13 @@ Capybara.register_driver :selenium do |app|
480
482
  Capybara::Selenium::Driver.new(app)
481
483
  end
482
484
 
485
+ Capybara.register_driver :selenium_chrome do |app|
486
+ Capybara::Selenium::Driver.new(app, :browser => :chrome)
487
+ end
488
+
489
+ Capybara.register_driver :selenium_chrome_headless do |app|
490
+ browser_options = ::Selenium::WebDriver::Chrome::Options.new()
491
+ browser_options.args << '--headless'
492
+ browser_options.args << '--disable-gpu'
493
+ Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
494
+ end
@@ -51,15 +51,25 @@ module Capybara
51
51
  # Set the server to use.
52
52
  #
53
53
  # Capybara.server = :webrick
54
+ # Capybara.server = :puma, { Silent: true }
54
55
  #
55
- # @param [Symbol] name Name of the server type to use
56
+ # @overload server=(name)
57
+ # @param [Symbol] name Name of the server type to use
58
+ # @overload server=([name, options])
59
+ # @param [Symbol] name Name of the server type to use
60
+ # @param [Hash] options Options to pass to the server block
56
61
  # @see register_server
57
62
  #
58
63
  def server=(name)
64
+ name, options = *name if name.is_a? Array
59
65
  @server = if name.respond_to? :call
60
66
  name
61
67
  else
62
- Capybara.servers[name.to_sym]
68
+ if options
69
+ Proc.new { |app, port, host| Capybara.servers[name.to_sym].call(app,port,host,options) }
70
+ else
71
+ Capybara.servers[name.to_sym]
72
+ end
63
73
  end
64
74
  end
65
75
 
@@ -10,6 +10,10 @@ class Capybara::Driver::Base
10
10
  raise NotImplementedError
11
11
  end
12
12
 
13
+ def refresh
14
+ raise NotImplementedError
15
+ end
16
+
13
17
  def find_xpath(query)
14
18
  raise NotImplementedError
15
19
  end
@@ -34,22 +34,66 @@ module Capybara
34
34
  else
35
35
  args.push(session_options: session_options)
36
36
  end
37
- query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
38
- synchronize(query.wait) do
39
- if (query.match == :smart or query.match == :prefer_exact)
40
- result = query.resolve_for(self, true)
41
- result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
42
- else
43
- result = query.resolve_for(self)
44
- end
45
- if query.match == :one or query.match == :smart and result.size > 1
46
- raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
47
- end
48
- if result.empty?
49
- raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
50
- end
51
- result.first
52
- end.tap(&:allow_reload!)
37
+ synced_resolve Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
38
+ end
39
+
40
+ ##
41
+ #
42
+ # Find an {Capybara::Node::Element} based on the given arguments that is also an ancestor of the element called on. +ancestor+ will raise an error if the element
43
+ # is not found.
44
+ #
45
+ # +ancestor+ takes the same options as +find+.
46
+ #
47
+ # element.ancestor('#foo').find('.bar')
48
+ # element.ancestor(:xpath, './/div[contains(., "bar")]')
49
+ # element.ancestor('ul', text: 'Quox').click_link('Delete')
50
+ #
51
+ # @param (see Capybara::Node::Finders#find)
52
+ #
53
+ # @!macro waiting_behavior
54
+ #
55
+ # @option options [Boolean] match The matching strategy to use.
56
+ #
57
+ # @return [Capybara::Node::Element] The found element
58
+ # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
59
+ #
60
+ def ancestor(*args, &optional_filter_block)
61
+ if args.last.is_a? Hash
62
+ args.last[:session_options] = session_options
63
+ else
64
+ args.push(session_options: session_options)
65
+ end
66
+ synced_resolve Capybara::Queries::AncestorQuery.new(*args, &optional_filter_block)
67
+ end
68
+
69
+ ##
70
+ #
71
+ # Find an {Capybara::Node::Element} based on the given arguments that is also a sibling of the element called on. +sibling+ will raise an error if the element
72
+ # is not found.
73
+ #
74
+ #
75
+ # +sibling+ takes the same options as +find+.
76
+ #
77
+ # element.sibling('#foo').find('.bar')
78
+ # element.sibling(:xpath, './/div[contains(., "bar")]')
79
+ # element.sibling('ul', text: 'Quox').click_link('Delete')
80
+ #
81
+ # @param (see Capybara::Node::Finders#find)
82
+ #
83
+ # @macro waiting_behavior
84
+ #
85
+ # @option options [Boolean] match The matching strategy to use.
86
+ #
87
+ # @return [Capybara::Node::Element] The found element
88
+ # @raise [Capybara::ElementNotFound] If the element can't be found before time expires
89
+ #
90
+ def sibling(*args, &optional_filter_block)
91
+ if args.last.is_a? Hash
92
+ args.last[:session_options] = session_options
93
+ else
94
+ args.push(session_options: session_options)
95
+ end
96
+ synced_resolve Capybara::Queries::SiblingQuery.new(*args, &optional_filter_block)
53
97
  end
54
98
 
55
99
  ##
@@ -252,6 +296,25 @@ module Capybara
252
296
  nil
253
297
  end
254
298
 
299
+ private
300
+
301
+ def synced_resolve(query)
302
+ synchronize(query.wait) do
303
+ if (query.match == :smart or query.match == :prefer_exact)
304
+ result = query.resolve_for(self, true)
305
+ result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
306
+ else
307
+ result = query.resolve_for(self)
308
+ end
309
+ if query.match == :one or query.match == :smart and result.size > 1
310
+ raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
311
+ end
312
+ if result.empty?
313
+ raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
314
+ end
315
+ result.first
316
+ end.tap(&:allow_reload!)
317
+ end
255
318
  end
256
319
  end
257
320
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Capybara
3
+ module Queries
4
+ class AncestorQuery < MatchQuery
5
+ # @api private
6
+ def resolve_for(node, exact = nil)
7
+ @resolved_node = node
8
+ node.synchronize do
9
+ match_results = super(node.session.current_scope, exact)
10
+ node.all(:xpath, XPath.ancestor) do |el|
11
+ match_results.include?(el)
12
+ end
13
+ end
14
+ end
15
+
16
+ def description
17
+ desc = super
18
+ if @resolved_node && (child_query = @resolved_node.instance_variable_get(:@query))
19
+ desc += " that is an ancestor of #{child_query.description}"
20
+ end
21
+ desc
22
+ end
23
+ end
24
+ end
25
+ end
@@ -43,7 +43,10 @@ module Capybara
43
43
  def label; selector.label or selector.name; end
44
44
 
45
45
  def description
46
- @description = String.new("#{label} #{locator.inspect}")
46
+ @description = String.new()
47
+ @description << "visible " if visible == :visible
48
+ @description << "non-visible " if visible == :hidden
49
+ @description << "#{label} #{locator.inspect}"
47
50
  @description << " with#{" exact" if exact_text == true} text #{options[:text].inspect}" if options[:text]
48
51
  @description << " with exact text #{options[:exact_text]}" if options[:exact_text].is_a?(String)
49
52
  @description << " with id #{options[:id]}" if options[:id]
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module Capybara
3
+ module Queries
4
+ class SiblingQuery < MatchQuery
5
+ # @api private
6
+ def resolve_for(node, exact = nil)
7
+ @resolved_node = node
8
+ node.synchronize do
9
+ match_results = super(node.session.current_scope, exact)
10
+ node.all(:xpath, XPath.preceding_sibling.union(XPath.following_sibling)) do |el|
11
+ match_results.include?(el)
12
+ end
13
+ end
14
+ end
15
+
16
+ def description
17
+ desc = super
18
+ if @resolved_node && (child_query = @resolved_node.instance_variable_get(:@query))
19
+ desc += " that is a sibling of #{child_query.description}"
20
+ end
21
+ desc
22
+ end
23
+ end
24
+ end
25
+ end
@@ -22,6 +22,11 @@ class Capybara::RackTest::Browser
22
22
  process_and_follow_redirects(:get, path, attributes)
23
23
  end
24
24
 
25
+ def refresh
26
+ reset_cache!
27
+ request(last_request.fullpath, last_request.env)
28
+ end
29
+
25
30
  def submit(method, path, attributes)
26
31
  path = request_path if not path or path.empty?
27
32
  process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rack/test'
3
3
  require 'rack/utils'
4
- require 'mime/types'
4
+ require 'mini_mime'
5
5
  require 'nokogiri'
6
6
  require 'cgi'
7
7
 
@@ -44,6 +44,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
44
44
  browser.visit(path, attributes)
45
45
  end
46
46
 
47
+ def refresh
48
+ browser.refresh
49
+ end
50
+
47
51
  def submit(method, path, attributes)
48
52
  browser.submit(method, path, attributes)
49
53
  end
@@ -42,9 +42,7 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
42
42
  if (value = field['value']).to_s.empty?
43
43
  NilUploadedFile.new
44
44
  else
45
- types = MIME::Types.type_for(value)
46
- content_type = types.sort_by.with_index { |type, idx| [type.obsolete? ? 1 : 0, idx] }.first.to_s
47
- Rack::Test::UploadedFile.new(value, content_type)
45
+ Rack::Test::UploadedFile.new(value, MiniMime.lookup_by_filename(value).content_type)
48
46
  end
49
47
  merge_param!(params, field['name'].to_s, file)
50
48
  else
@@ -141,7 +141,7 @@ private
141
141
 
142
142
  # a reference to the select node if this is an option node
143
143
  def select_node
144
- find_xpath('./ancestor::select').first
144
+ find_xpath('./ancestor::select[1]').first
145
145
  end
146
146
 
147
147
  def type
@@ -0,0 +1,95 @@
1
+ module Capybara
2
+ module RSpecMatchers
3
+ module Compound
4
+ include ::RSpec::Matchers::Composable
5
+
6
+ def and(matcher)
7
+ Capybara::RSpecMatchers::Compound::And.new(self,matcher)
8
+ end
9
+
10
+ def and_then(matcher)
11
+ ::RSpec::Matchers::BuiltIn::Compound::And.new(self, matcher)
12
+ end
13
+
14
+ def or(matcher)
15
+ Capybara::RSpecMatchers::Compound::Or.new(self, matcher)
16
+ end
17
+
18
+
19
+ class CapybaraEvaluator
20
+ def initialize(actual, matcher_1, matcher_2)
21
+ @actual = actual
22
+ @matcher_1 = matcher_1
23
+ @matcher_2 = matcher_2
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
34
+ end
35
+
36
+ class And < ::RSpec::Matchers::BuiltIn::Compound::And
37
+
38
+ private
39
+
40
+ def match(_expected, actual)
41
+ @evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
42
+ syncer = sync_element(actual)
43
+ begin
44
+ syncer.synchronize do
45
+ @evaluator.reset
46
+ raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].all?
47
+ true
48
+ end
49
+ rescue
50
+ false
51
+ end
52
+ end
53
+
54
+ def sync_element(el)
55
+ if el.respond_to? :synchronize
56
+ el
57
+ elsif el.respond_to? :current_scope
58
+ el.current_scope
59
+ else
60
+ Capybara.string(el)
61
+ end
62
+ end
63
+ end
64
+
65
+ class Or < ::RSpec::Matchers::BuiltIn::Compound::Or
66
+
67
+ private
68
+
69
+ def match(_expected, actual)
70
+ @evaluator = CapybaraEvaluator.new(actual, matcher_1, matcher_2)
71
+ syncer = sync_element(actual)
72
+ begin
73
+ syncer.synchronize do
74
+ @evaluator.reset
75
+ raise ::Capybara::ElementNotFound unless [matcher_1_matches?, matcher_2_matches?].any?
76
+ true
77
+ end
78
+ rescue
79
+ false
80
+ end
81
+ end
82
+
83
+ def sync_element(el)
84
+ if el.respond_to? :synchronize
85
+ el
86
+ elsif el.respond_to? :current_scope
87
+ el.current_scope
88
+ else
89
+ Capybara.string(el)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end