capybara 2.13.0 → 2.14.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 +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
|