capybara 2.13.0 → 2.14.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 +82 -17
- data/README.md +29 -0
- data/lib/capybara.rb +81 -116
- data/lib/capybara/config.rb +121 -0
- data/lib/capybara/cucumber.rb +1 -0
- data/lib/capybara/driver/base.rb +6 -0
- data/lib/capybara/dsl.rb +1 -3
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/node/actions.rb +2 -2
- data/lib/capybara/node/base.rb +7 -2
- data/lib/capybara/node/element.rb +7 -1
- data/lib/capybara/node/finders.rb +13 -3
- data/lib/capybara/node/matchers.rb +15 -4
- data/lib/capybara/node/simple.rb +5 -0
- data/lib/capybara/queries/base_query.rb +8 -3
- data/lib/capybara/queries/selector_query.rb +11 -9
- data/lib/capybara/queries/text_query.rb +9 -4
- data/lib/capybara/rack_test/browser.rb +8 -5
- data/lib/capybara/rspec.rb +3 -1
- data/lib/capybara/rspec/matcher_proxies.rb +41 -0
- data/lib/capybara/rspec/matchers.rb +19 -5
- data/lib/capybara/selector.rb +13 -4
- data/lib/capybara/selector/selector.rb +3 -3
- data/lib/capybara/selenium/driver.rb +20 -6
- data/lib/capybara/selenium/node.rb +6 -2
- data/lib/capybara/server.rb +6 -5
- data/lib/capybara/session.rb +71 -14
- data/lib/capybara/session/config.rb +100 -0
- data/lib/capybara/spec/public/test.js +1 -1
- data/lib/capybara/spec/session/all_spec.rb +11 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/find_field_spec.rb +1 -0
- data/lib/capybara/spec/session/find_spec.rb +4 -3
- data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
- data/lib/capybara/spec/session/node_spec.rb +23 -17
- data/lib/capybara/spec/session/reset_session_spec.rb +1 -1
- data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +22 -0
- data/lib/capybara/spec/views/form.erb +6 -1
- data/lib/capybara/spec/views/with_html.erb +1 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/capybara_spec.rb +14 -2
- data/spec/dsl_spec.rb +1 -0
- data/spec/per_session_config_spec.rb +67 -0
- data/spec/rspec/shared_spec_matchers.rb +2 -2
- data/spec/rspec/views_spec.rb +4 -0
- data/spec/rspec_spec.rb +77 -0
- data/spec/session_spec.rb +44 -0
- data/spec/shared_selenium_session.rb +9 -0
- data/spec/spec_helper.rb +4 -0
- metadata +7 -3
@@ -5,13 +5,18 @@ module Capybara
|
|
5
5
|
class TextQuery < BaseQuery
|
6
6
|
def initialize(*args)
|
7
7
|
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
8
|
-
@type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
|
-
@
|
8
|
+
# @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
|
+
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
10
|
+
self.session_options = @options.delete(:session_options)
|
11
|
+
|
12
|
+
@type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
|
13
|
+
|
14
|
+
@expected_text = args.shift
|
10
15
|
unless @expected_text.is_a?(Regexp)
|
11
16
|
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
12
17
|
end
|
13
|
-
@options ||= {}
|
14
18
|
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
19
|
+
warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
|
15
20
|
assert_valid_keys
|
16
21
|
end
|
17
22
|
|
@@ -40,7 +45,7 @@ module Capybara
|
|
40
45
|
private
|
41
46
|
|
42
47
|
def exact?
|
43
|
-
options.fetch(:exact,
|
48
|
+
options.fetch(:exact, session_options.exact_text)
|
44
49
|
end
|
45
50
|
|
46
51
|
def build_message(report_on_invisible)
|
@@ -45,10 +45,13 @@ class Capybara::RackTest::Browser
|
|
45
45
|
def process(method, path, attributes = {}, env = {})
|
46
46
|
new_uri = URI.parse(path)
|
47
47
|
method.downcase! unless method.is_a? Symbol
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
if path.empty?
|
49
|
+
new_uri.path = request_path
|
50
|
+
else
|
51
|
+
new_uri.path = request_path if path.start_with?("?")
|
52
|
+
new_uri.path = "/" if new_uri.path.empty?
|
53
|
+
new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
|
54
|
+
end
|
52
55
|
new_uri.scheme ||= @current_scheme
|
53
56
|
new_uri.host ||= @current_host
|
54
57
|
new_uri.port ||= @current_port unless new_uri.default_port == @current_port
|
@@ -68,7 +71,7 @@ class Capybara::RackTest::Browser
|
|
68
71
|
end
|
69
72
|
|
70
73
|
def reset_host!
|
71
|
-
uri = URI.parse(
|
74
|
+
uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
|
72
75
|
@current_scheme = uri.scheme
|
73
76
|
@current_host = uri.host
|
74
77
|
@current_port = uri.port
|
data/lib/capybara/rspec.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'capybara/dsl'
|
3
2
|
require 'rspec/core'
|
3
|
+
require 'capybara/dsl'
|
4
4
|
require 'capybara/rspec/matchers'
|
5
5
|
require 'capybara/rspec/features'
|
6
|
+
require 'capybara/rspec/matcher_proxies'
|
6
7
|
|
7
8
|
RSpec.configure do |config|
|
8
9
|
config.include Capybara::DSL, :type => :feature
|
@@ -22,6 +23,7 @@ RSpec.configure do |config|
|
|
22
23
|
Capybara.use_default_driver
|
23
24
|
end
|
24
25
|
end
|
26
|
+
|
25
27
|
config.before do
|
26
28
|
if self.class.include?(Capybara::DSL)
|
27
29
|
example = fetch_current_example.call(self)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Capybara
|
3
|
+
module RSpecMatcherProxies
|
4
|
+
def all(*args)
|
5
|
+
if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
|
6
|
+
::RSpec::Matchers::BuiltIn::All.new(*args)
|
7
|
+
else
|
8
|
+
find_all(*args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def within(*args)
|
13
|
+
if block_given?
|
14
|
+
within_element(*args, &Proc.new)
|
15
|
+
else
|
16
|
+
be_within(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module DSL
|
22
|
+
def self.included(base)
|
23
|
+
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
|
24
|
+
|
25
|
+
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
|
26
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies)
|
27
|
+
end
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if defined?(::RSpec::Matchers)
|
35
|
+
module ::RSpec::Matchers
|
36
|
+
def self.included(base)
|
37
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies) if base.include?(::Capybara::DSL)
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -7,7 +7,7 @@ module Capybara
|
|
7
7
|
attr_reader :failure_message, :failure_message_when_negated
|
8
8
|
|
9
9
|
def wrap(actual)
|
10
|
-
if actual.respond_to?("has_selector?")
|
10
|
+
@context_el = if actual.respond_to?("has_selector?")
|
11
11
|
actual
|
12
12
|
else
|
13
13
|
Capybara.string(actual.to_s)
|
@@ -33,6 +33,19 @@ module Capybara
|
|
33
33
|
@failure_message_when_negated = e.message
|
34
34
|
return false
|
35
35
|
end
|
36
|
+
|
37
|
+
def session_query_args
|
38
|
+
if @args.last.is_a? Hash
|
39
|
+
@args.last[:session_options] = session_options
|
40
|
+
else
|
41
|
+
@args.push(session_options: session_options)
|
42
|
+
end
|
43
|
+
@args
|
44
|
+
end
|
45
|
+
|
46
|
+
def session_options
|
47
|
+
@context_el ? @context_el.session_options : Capybara.session_options
|
48
|
+
end
|
36
49
|
end
|
37
50
|
|
38
51
|
class HaveSelector < Matcher
|
@@ -55,7 +68,7 @@ module Capybara
|
|
55
68
|
end
|
56
69
|
|
57
70
|
def query
|
58
|
-
@query ||= Capybara::Queries::SelectorQuery.new(
|
71
|
+
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
|
59
72
|
end
|
60
73
|
end
|
61
74
|
|
@@ -73,7 +86,7 @@ module Capybara
|
|
73
86
|
end
|
74
87
|
|
75
88
|
def query
|
76
|
-
@query ||= Capybara::Queries::MatchQuery.new(
|
89
|
+
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
|
77
90
|
end
|
78
91
|
end
|
79
92
|
|
@@ -155,11 +168,12 @@ module Capybara
|
|
155
168
|
|
156
169
|
class BecomeClosed
|
157
170
|
def initialize(options)
|
158
|
-
@
|
171
|
+
@options = options
|
159
172
|
end
|
160
173
|
|
161
174
|
def matches?(window)
|
162
175
|
@window = window
|
176
|
+
@wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
|
163
177
|
start_time = Capybara::Helpers.monotonic_time
|
164
178
|
while window.exists?
|
165
179
|
return false if (Capybara::Helpers.monotonic_time - start_time) > @wait_time
|
@@ -267,4 +281,4 @@ module Capybara
|
|
267
281
|
BecomeClosed.new(options)
|
268
282
|
end
|
269
283
|
end
|
270
|
-
end
|
284
|
+
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -139,7 +139,7 @@ Capybara.add_selector(:link) do
|
|
139
139
|
XPath.string.n.is(locator) |
|
140
140
|
XPath.attr(:title).is(locator) |
|
141
141
|
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
142
|
-
matchers |= XPath.attr(:'aria-label').is(locator) if
|
142
|
+
matchers |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
143
143
|
xpath = xpath[matchers]
|
144
144
|
end
|
145
145
|
xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
@@ -185,14 +185,14 @@ Capybara.add_selector(:button) do
|
|
185
185
|
unless locator.nil?
|
186
186
|
locator = locator.to_s
|
187
187
|
locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
188
|
-
locator_matches |= XPath.attr(:'aria-label').is(locator) if
|
188
|
+
locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
189
189
|
|
190
190
|
input_btn_xpath = input_btn_xpath[locator_matches]
|
191
191
|
|
192
192
|
btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
193
193
|
|
194
194
|
alt_matches = XPath.attr(:alt).is(locator)
|
195
|
-
alt_matches |= XPath.attr(:'aria-label').is(locator) if
|
195
|
+
alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
196
196
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
197
197
|
end
|
198
198
|
|
@@ -237,14 +237,23 @@ end
|
|
237
237
|
# @filter [String] :name Matches the name attribute
|
238
238
|
# @filter [String] :placeholder Matches the placeholder attribute
|
239
239
|
# @filter [String] :with Matches the current value of the field
|
240
|
+
# @filter [String] :type Matches the type attribute of the field or element type for 'textarea'
|
240
241
|
# @filter [String, Array<String>] :class Matches the class(es) provided
|
241
242
|
# @filter [Boolean] :disabled Match disabled field?
|
242
243
|
# @filter [Boolean] :multiple Match fields that accept multiple values
|
243
244
|
#
|
244
245
|
Capybara.add_selector(:fillable_field) do
|
245
246
|
label "field"
|
246
|
-
xpath(:name, :placeholder) do |locator, options|
|
247
|
+
xpath(:name, :placeholder, :type) do |locator, options|
|
247
248
|
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
249
|
+
if options[:type]
|
250
|
+
type=options[:type].to_s
|
251
|
+
if ['textarea'].include?(type)
|
252
|
+
xpath = XPath.descendant(type.to_sym)
|
253
|
+
else
|
254
|
+
xpath = xpath[XPath.attr(:type).equals(type)]
|
255
|
+
end
|
256
|
+
end
|
248
257
|
locate_field(xpath, locator, options)
|
249
258
|
end
|
250
259
|
|
@@ -201,9 +201,9 @@ module Capybara
|
|
201
201
|
@default_visibility = default_visibility
|
202
202
|
end
|
203
203
|
|
204
|
-
def default_visibility
|
204
|
+
def default_visibility(fallback = Capybara.ignore_hidden_elements)
|
205
205
|
if @default_visibility.nil?
|
206
|
-
|
206
|
+
fallback
|
207
207
|
else
|
208
208
|
@default_visibility
|
209
209
|
end
|
@@ -219,7 +219,7 @@ module Capybara
|
|
219
219
|
XPath.attr(:name).equals(locator) |
|
220
220
|
XPath.attr(:placeholder).equals(locator) |
|
221
221
|
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))
|
222
|
-
attr_matchers |= XPath.attr(:'aria-label').is(locator) if
|
222
|
+
attr_matchers |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
223
223
|
|
224
224
|
locate_xpath = locate_xpath[attr_matchers]
|
225
225
|
locate_xpath += XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
|
@@ -35,6 +35,13 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
35
35
|
def initialize(app, options={})
|
36
36
|
begin
|
37
37
|
require 'selenium-webdriver'
|
38
|
+
# Fix for selenium-webdriver 3.4.0 which misnamed these
|
39
|
+
if !defined?(::Selenium::WebDriver::Error::ElementNotInteractableError)
|
40
|
+
::Selenium::WebDriver::Error.const_set('ElementNotInteractableError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
41
|
+
end
|
42
|
+
if !defined?(::Selenium::WebDriver::Error::ElementClickInterceptedError)
|
43
|
+
::Selenium::WebDriver::Error.const_set('ElementClickInterceptedError', Class.new(::Selenium::WebDriver::Error::WebDriverError))
|
44
|
+
end
|
38
45
|
rescue LoadError => e
|
39
46
|
if e.message =~ /selenium-webdriver/
|
40
47
|
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
@@ -185,7 +192,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
185
192
|
|
186
193
|
def resize_window_to(handle, width, height)
|
187
194
|
within_given_window(handle) do
|
188
|
-
|
195
|
+
# Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643
|
196
|
+
if marionette? && (window_size(handle) == [width, height])
|
197
|
+
{}
|
198
|
+
else
|
199
|
+
browser.manage.window.resize_to(width, height)
|
200
|
+
end
|
189
201
|
end
|
190
202
|
end
|
191
203
|
|
@@ -250,10 +262,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
250
262
|
end
|
251
263
|
|
252
264
|
def invalid_element_errors
|
253
|
-
[Selenium::WebDriver::Error::StaleElementReferenceError,
|
254
|
-
Selenium::WebDriver::Error::UnhandledError,
|
255
|
-
Selenium::WebDriver::Error::ElementNotVisibleError,
|
256
|
-
Selenium::WebDriver::Error::InvalidSelectorError
|
265
|
+
[::Selenium::WebDriver::Error::StaleElementReferenceError,
|
266
|
+
::Selenium::WebDriver::Error::UnhandledError,
|
267
|
+
::Selenium::WebDriver::Error::ElementNotVisibleError,
|
268
|
+
::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a race condition that can occur with chromedriver and #go_back/#go_forward
|
269
|
+
::Selenium::WebDriver::Error::ElementNotInteractableError,
|
270
|
+
::Selenium::WebDriver::Error::ElementClickInterceptedError]
|
257
271
|
end
|
258
272
|
|
259
273
|
def no_such_window_error
|
@@ -306,7 +320,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
306
320
|
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
307
321
|
# Actual wait time may be longer than specified
|
308
322
|
wait = Selenium::WebDriver::Wait.new(
|
309
|
-
timeout: (
|
323
|
+
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
310
324
|
ignore: Selenium::WebDriver::Error::NoAlertPresentError)
|
311
325
|
begin
|
312
326
|
wait.until do
|
@@ -77,16 +77,20 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
77
77
|
elsif native.attribute('isContentEditable')
|
78
78
|
#ensure we are focused on the element
|
79
79
|
native.click
|
80
|
+
|
80
81
|
script = <<-JS
|
81
82
|
var range = document.createRange();
|
83
|
+
var sel = window.getSelection();
|
82
84
|
arguments[0].focus();
|
83
85
|
range.selectNodeContents(arguments[0]);
|
84
|
-
|
86
|
+
sel.removeAllRanges();
|
87
|
+
sel.addRange(range);
|
85
88
|
JS
|
86
89
|
driver.execute_script script, self
|
90
|
+
|
87
91
|
if (driver.options[:browser].to_s == "chrome") ||
|
88
92
|
(driver.options[:browser].to_s == "firefox" && !driver.marionette?)
|
89
|
-
# chromedriver raises a can't focus element if we use native.send_keys
|
93
|
+
# chromedriver raises a can't focus element for child elements if we use native.send_keys
|
90
94
|
# we've already focused it so just use action api
|
91
95
|
driver.browser.action.send_keys(value.to_s).perform
|
92
96
|
else
|
data/lib/capybara/server.rb
CHANGED
@@ -25,9 +25,10 @@ module Capybara
|
|
25
25
|
|
26
26
|
attr_accessor :error
|
27
27
|
|
28
|
-
def initialize(app)
|
28
|
+
def initialize(app, server_errors)
|
29
29
|
@app = app
|
30
30
|
@counter = Counter.new
|
31
|
+
@server_errors = server_errors
|
31
32
|
end
|
32
33
|
|
33
34
|
def pending_requests?
|
@@ -41,7 +42,7 @@ module Capybara
|
|
41
42
|
@counter.increment
|
42
43
|
begin
|
43
44
|
@app.call(env)
|
44
|
-
rescue
|
45
|
+
rescue *@server_errors => e
|
45
46
|
@error = e unless @error
|
46
47
|
raise e
|
47
48
|
ensure
|
@@ -59,10 +60,10 @@ module Capybara
|
|
59
60
|
|
60
61
|
attr_reader :app, :port, :host
|
61
62
|
|
62
|
-
def initialize(app, port=Capybara.server_port, host=Capybara.server_host)
|
63
|
+
def initialize(app, port=Capybara.server_port, host=Capybara.server_host, server_errors=Capybara.server_errors)
|
63
64
|
@app = app
|
64
65
|
@server_thread = nil # suppress warnings
|
65
|
-
@host, @port = host, port
|
66
|
+
@host, @port, @server_errors = host, port, server_errors
|
66
67
|
@port ||= Capybara::Server.ports[port_key]
|
67
68
|
@port ||= find_available_port(host)
|
68
69
|
end
|
@@ -112,7 +113,7 @@ module Capybara
|
|
112
113
|
private
|
113
114
|
|
114
115
|
def middleware
|
115
|
-
@middleware ||= Middleware.new(app)
|
116
|
+
@middleware ||= Middleware.new(app, @server_errors)
|
116
117
|
end
|
117
118
|
|
118
119
|
def port_key
|
data/lib/capybara/session.rb
CHANGED
@@ -17,6 +17,14 @@ module Capybara
|
|
17
17
|
# session = Capybara::Session.new(:culerity)
|
18
18
|
# session.visit('http://www.google.com')
|
19
19
|
#
|
20
|
+
# When Capybara.threadsafe == true the sessions options will be initially set to the
|
21
|
+
# current values of the global options and a configuration block can be passed to the session initializer.
|
22
|
+
# For available options see {Capybara::SessionConfig::OPTIONS}
|
23
|
+
#
|
24
|
+
# session = Capybara::Session.new(:driver, MyRackApp) do |config|
|
25
|
+
# config.app_host = "http://my_host.dev"
|
26
|
+
# end
|
27
|
+
#
|
20
28
|
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
|
21
29
|
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
|
22
30
|
# the current HTML document. This allows interaction:
|
@@ -69,10 +77,15 @@ module Capybara
|
|
69
77
|
|
70
78
|
def initialize(mode, app=nil)
|
71
79
|
raise TypeError, "The second parameter to Session::new should be a rack app if passed." if app && !app.respond_to?(:call)
|
80
|
+
@@instance_created = true
|
72
81
|
@mode = mode
|
73
82
|
@app = app
|
74
|
-
if
|
75
|
-
|
83
|
+
if block_given?
|
84
|
+
raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
|
85
|
+
yield config if block_given?
|
86
|
+
end
|
87
|
+
if config.run_server and @app and driver.needs_server?
|
88
|
+
@server = Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
|
76
89
|
else
|
77
90
|
@server = nil
|
78
91
|
end
|
@@ -85,7 +98,9 @@ module Capybara
|
|
85
98
|
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
86
99
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
87
100
|
end
|
88
|
-
Capybara.drivers[mode].call(app)
|
101
|
+
driver = Capybara.drivers[mode].call(app)
|
102
|
+
driver.session = self if driver.respond_to?(:session=)
|
103
|
+
driver
|
89
104
|
end
|
90
105
|
end
|
91
106
|
|
@@ -126,7 +141,7 @@ module Capybara
|
|
126
141
|
if @server and @server.error
|
127
142
|
# Force an explanation for the error being raised as the exception cause
|
128
143
|
begin
|
129
|
-
if
|
144
|
+
if config.raise_server_errors
|
130
145
|
raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
|
131
146
|
end
|
132
147
|
rescue CapybaraError
|
@@ -235,10 +250,10 @@ module Capybara
|
|
235
250
|
visit_uri = URI.parse(visit_uri.to_s)
|
236
251
|
|
237
252
|
uri_base = if @server
|
238
|
-
visit_uri.port = @server.port if
|
239
|
-
URI.parse(
|
253
|
+
visit_uri.port = @server.port if config.always_include_port && (visit_uri.port == visit_uri.default_port)
|
254
|
+
URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
|
240
255
|
else
|
241
|
-
|
256
|
+
config.app_host && URI.parse(config.app_host)
|
242
257
|
end
|
243
258
|
|
244
259
|
# TODO - this is only for compatability with previous 2.x behavior that concatenated
|
@@ -481,7 +496,7 @@ module Capybara
|
|
481
496
|
driver.switch_to_window(window.handle)
|
482
497
|
window
|
483
498
|
else
|
484
|
-
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
499
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
485
500
|
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
486
501
|
original_window_handle = driver.current_window_handle
|
487
502
|
begin
|
@@ -578,7 +593,7 @@ module Capybara
|
|
578
593
|
old_handles = driver.window_handles
|
579
594
|
block.call
|
580
595
|
|
581
|
-
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
596
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
582
597
|
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
583
598
|
opened_handles = (driver.window_handles - old_handles)
|
584
599
|
if opened_handles.size != 1
|
@@ -701,7 +716,7 @@ module Capybara
|
|
701
716
|
#
|
702
717
|
def save_page(path = nil)
|
703
718
|
path = prepare_path(path, 'html')
|
704
|
-
File.write(path, Capybara::Helpers.inject_asset_host(body), mode: 'wb')
|
719
|
+
File.write(path, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
|
705
720
|
path
|
706
721
|
end
|
707
722
|
|
@@ -786,7 +801,49 @@ module Capybara
|
|
786
801
|
scope
|
787
802
|
end
|
788
803
|
|
804
|
+
##
|
805
|
+
#
|
806
|
+
# Yield a block using a specific wait time
|
807
|
+
#
|
808
|
+
def using_wait_time(seconds)
|
809
|
+
if Capybara.threadsafe
|
810
|
+
begin
|
811
|
+
previous_wait_time = config.default_max_wait_time
|
812
|
+
config.default_max_wait_time = seconds
|
813
|
+
yield
|
814
|
+
ensure
|
815
|
+
config.default_max_wait_time = previous_wait_time
|
816
|
+
end
|
817
|
+
else
|
818
|
+
Capybara.using_wait_time(seconds) { yield }
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
##
|
823
|
+
#
|
824
|
+
# Accepts a block to set the configuration options if Capybara.threadsafe == true. Note that some options only have an effect
|
825
|
+
# if set at initialization time, so look at the configuration block that can be passed to the initializer too
|
826
|
+
#
|
827
|
+
def configure
|
828
|
+
raise "Session configuration is only supported when Capybara.threadsafe == true" unless Capybara.threadsafe
|
829
|
+
yield config
|
830
|
+
end
|
831
|
+
|
832
|
+
def self.instance_created?
|
833
|
+
@@instance_created
|
834
|
+
end
|
835
|
+
|
836
|
+
def config
|
837
|
+
@config ||= if Capybara.threadsafe
|
838
|
+
Capybara.session_options.dup
|
839
|
+
else
|
840
|
+
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
841
|
+
end
|
842
|
+
end
|
789
843
|
private
|
844
|
+
|
845
|
+
@@instance_created = false
|
846
|
+
|
790
847
|
def accept_modal(type, text_or_options, options, &blk)
|
791
848
|
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
792
849
|
end
|
@@ -798,7 +855,7 @@ module Capybara
|
|
798
855
|
def modal_options(text_or_options, options)
|
799
856
|
text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
|
800
857
|
options[:text] ||= text_or_options unless text_or_options.nil?
|
801
|
-
options[:wait] ||=
|
858
|
+
options[:wait] ||= config.default_max_wait_time
|
802
859
|
options
|
803
860
|
end
|
804
861
|
|
@@ -814,10 +871,10 @@ module Capybara
|
|
814
871
|
end
|
815
872
|
|
816
873
|
def prepare_path(path, extension)
|
817
|
-
if
|
818
|
-
path = File.expand_path(path || default_fn(extension),
|
874
|
+
if config.save_path || config.save_and_open_page_path.nil?
|
875
|
+
path = File.expand_path(path || default_fn(extension), config.save_path)
|
819
876
|
else
|
820
|
-
path = File.expand_path(default_fn(extension),
|
877
|
+
path = File.expand_path(default_fn(extension), config.save_and_open_page_path) if path.nil?
|
821
878
|
end
|
822
879
|
FileUtils.mkdir_p(File.dirname(path))
|
823
880
|
path
|