capybara 2.14.4 → 2.15.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 +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
|