capybara 2.14.4 → 2.15.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 +17 -0
- data/README.md +14 -22
- data/lib/capybara.rb +16 -4
- data/lib/capybara/config.rb +12 -2
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/node/finders.rb +79 -16
- data/lib/capybara/queries/ancestor_query.rb +25 -0
- data/lib/capybara/queries/selector_query.rb +4 -1
- data/lib/capybara/queries/sibling_query.rb +25 -0
- data/lib/capybara/rack_test/browser.rb +5 -0
- data/lib/capybara/rack_test/driver.rb +5 -1
- data/lib/capybara/rack_test/form.rb +1 -3
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/rspec/compound.rb +95 -0
- data/lib/capybara/rspec/matchers.rb +4 -1
- data/lib/capybara/selector.rb +1 -0
- data/lib/capybara/selector/filter.rb +13 -41
- data/lib/capybara/selector/filter_set.rb +12 -5
- data/lib/capybara/selector/filters/base.rb +33 -0
- data/lib/capybara/selector/filters/expression_filter.rb +40 -0
- data/lib/capybara/selector/filters/node_filter.rb +27 -0
- data/lib/capybara/selector/selector.rb +4 -4
- data/lib/capybara/selenium/driver.rb +12 -2
- data/lib/capybara/selenium/node.rb +70 -55
- data/lib/capybara/session.rb +65 -41
- data/lib/capybara/spec/session/ancestor_spec.rb +85 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +1 -1
- data/lib/capybara/spec/session/check_spec.rb +4 -4
- data/lib/capybara/spec/session/choose_spec.rb +2 -2
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +3 -3
- data/lib/capybara/spec/session/click_link_spec.rb +1 -1
- data/lib/capybara/spec/session/fill_in_spec.rb +2 -2
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/refresh_spec.rb +28 -0
- data/lib/capybara/spec/session/select_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
- data/lib/capybara/spec/session/unselect_spec.rb +2 -2
- data/lib/capybara/spec/session/window/become_closed_spec.rb +3 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +11 -9
- data/lib/capybara/spec/session/window/within_window_spec.rb +27 -2
- data/lib/capybara/spec/test_app.rb +3 -1
- data/lib/capybara/spec/views/with_html.erb +27 -1
- data/lib/capybara/spec/views/with_windows.erb +4 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +9 -1
- data/spec/minitest_spec_spec.rb +1 -1
- data/spec/rspec/shared_spec_matchers.rb +146 -42
- data/spec/selenium_spec_chrome.rb +12 -16
- data/spec/selenium_spec_marionette.rb +2 -0
- data/spec/shared_selenium_session.rb +2 -0
- metadata +14 -6
- data/lib/capybara/selector/expression_filter.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e019a48b2f87598c4c62ba40e9fc12291b8f538
|
4
|
+
data.tar.gz: 2625bd193da4cdab59284069d1b5b2cdd8430b89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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.
|
data/lib/capybara.rb
CHANGED
@@ -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
|
data/lib/capybara/config.rb
CHANGED
@@ -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
|
-
# @
|
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
|
-
|
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
|
|
data/lib/capybara/driver/base.rb
CHANGED
@@ -34,22 +34,66 @@ module Capybara
|
|
34
34
|
else
|
35
35
|
args.push(session_options: session_options)
|
36
36
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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(
|
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 '
|
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
|
-
|
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
|
@@ -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
|