mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Per-session config and thread specific current_driver/session_name
This commit is contained in:
parent
7f8914b605
commit
0270b7a4cf
36 changed files with 705 additions and 204 deletions
26
README.md
26
README.md
|
@ -67,6 +67,7 @@ You can read more about the missing features [here](https://github.com/teamcapyb
|
|||
- [Beware the XPath // trap](#beware-the-xpath--trap)
|
||||
- [Configuring and adding drivers](#configuring-and-adding-drivers)
|
||||
- [Gotchas:](#gotchas)
|
||||
- ["Threadsafe" mode](#threadsafe)
|
||||
- [Development](#development)
|
||||
|
||||
## <a name="key-benefits"></a>Key benefits
|
||||
|
@ -1048,6 +1049,31 @@ additional info about how the underlying driver can be configured.
|
|||
are testing for specific server errors and using multiple sessions make sure to test for the
|
||||
errors using the initial session (usually :default)
|
||||
|
||||
## <a name="threadsafe"></a>"Threadsafe" mode - BETA - may change
|
||||
|
||||
In normal mode most of Capybara's configuration options are global settings which can cause issues
|
||||
if using multiple sessions and wanting to change a setting for only one of the sessions. To provide
|
||||
support for this type of usage Capybara now provides a "threadsafe" mode which can be enabled by setting
|
||||
|
||||
Capybara.threadsafe = true
|
||||
|
||||
This setting can only be changed before any sessions have been created. In "threadsafe" mode the following
|
||||
behaviors of Capybara change
|
||||
|
||||
* Most options can now be set on a session. These can either be set at session creation time or after, and
|
||||
default to the global options at the time of session creation. Options which are NOT session specific are
|
||||
`app`, `reuse_server`, `default_driver`, `javascript_driver`, and (obviously) `threadsafe`. Any drivers and servers
|
||||
registered through `register_driver` and `register_server` are also global.
|
||||
|
||||
my_session = Capybara::Session.new(:driver, some_app) do |config|
|
||||
config.automatic_label_click = true # only set for my_session
|
||||
end
|
||||
my_session.config.default_max_wait_time = 10 # only set for my_session
|
||||
Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session
|
||||
|
||||
* `current_driver` and `session_name` are thread specific. This means that `using_session' and
|
||||
`using_driver` also only affect the current thread.
|
||||
|
||||
## <a name="development"></a>Development
|
||||
|
||||
To set up a development environment, simply do:
|
||||
|
|
197
lib/capybara.rb
197
lib/capybara.rb
|
@ -2,6 +2,8 @@
|
|||
require 'timeout'
|
||||
require 'nokogiri'
|
||||
require 'xpath'
|
||||
require 'forwardable'
|
||||
require 'capybara/config'
|
||||
|
||||
module Capybara
|
||||
class CapybaraError < StandardError; end
|
||||
|
@ -19,18 +21,41 @@ module Capybara
|
|||
class WindowError < CapybaraError; end
|
||||
class ReadOnlyElementError < CapybaraError; end
|
||||
|
||||
|
||||
class << self
|
||||
attr_reader :app_host, :default_host
|
||||
attr_accessor :asset_host, :run_server, :always_include_port
|
||||
attr_accessor :server_port, :exact, :match, :exact_options, :visible_text_only, :enable_aria_label
|
||||
attr_accessor :default_selector, :default_max_wait_time, :ignore_hidden_elements
|
||||
attr_accessor :save_path, :wait_on_first_by_default, :automatic_label_click, :automatic_reload
|
||||
attr_reader :reuse_server
|
||||
attr_accessor :raise_server_errors, :server_errors
|
||||
attr_writer :default_driver, :current_driver, :javascript_driver, :session_name, :server_host
|
||||
attr_reader :save_and_open_page_path
|
||||
attr_accessor :exact_text
|
||||
attr_accessor :app
|
||||
extend Forwardable
|
||||
|
||||
# DelegateCapybara global configurations
|
||||
# @!method app
|
||||
# See {Capybara#configure}
|
||||
# @!method reuse_server
|
||||
# See {Capybara#configure}
|
||||
# @!method threadsafe
|
||||
# See {Capybara#configure}
|
||||
# @!method server
|
||||
# See {Capybara#configure}
|
||||
# @!method default_driver
|
||||
# See {Capybara#configure}
|
||||
# @!method javascript_driver
|
||||
# See {Capybara#configure}
|
||||
Config::OPTIONS.each do |method|
|
||||
def_delegators :config, method, "#{method}="
|
||||
end
|
||||
|
||||
# Delegate Capybara global configurations
|
||||
# @!method default_selector
|
||||
# See {Capybara#configure}
|
||||
# @!method default_max_wait_time
|
||||
# See {Capybara#configure}
|
||||
# @!method app_host
|
||||
# See {Capybara#configure}
|
||||
# @!method always_include_port
|
||||
# See {Capybara#configure}
|
||||
# @!method wait_on_first_by_default
|
||||
# See {Capybara#configure}
|
||||
SessionConfig::OPTIONS.each do |method|
|
||||
def_delegators :config, method, "#{method}="
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
|
@ -58,6 +83,9 @@ module Capybara
|
|||
# [automatic_label_click = Boolean] Whether Node#choose, Node#check, Node#uncheck will attempt to click the associated label element if the checkbox/radio button are non-visible (Default: false)
|
||||
# [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false)
|
||||
# [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true)
|
||||
# [threadsafe = Boolean] Whether sessions can be configured individually (Default: false)
|
||||
# [server = Symbol] The name of the registered server to use when running the app under test (Default: :webrick)
|
||||
#
|
||||
# === DSL Options
|
||||
#
|
||||
# when using capybara/dsl, the following options are also available:
|
||||
|
@ -66,7 +94,7 @@ module Capybara
|
|||
# [javascript_driver = Symbol] The name of a driver to use for JavaScript enabled tests. (Default: :selenium)
|
||||
#
|
||||
def configure
|
||||
yield self
|
||||
yield ConfigureDeprecator.new(config)
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -163,45 +191,6 @@ module Capybara
|
|||
@servers ||= {}
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Register a proc that Capybara will call to run the Rack application.
|
||||
#
|
||||
# Capybara.server do |app, port, host|
|
||||
# require 'rack/handler/mongrel'
|
||||
# Rack::Handler::Mongrel.run(app, :Port => port)
|
||||
# end
|
||||
#
|
||||
# By default, Capybara will try to run webrick.
|
||||
#
|
||||
# @yield [app, port, host] This block receives a rack app, port, and host/ip and should run a Rack handler
|
||||
#
|
||||
def server(&block)
|
||||
if block_given?
|
||||
warn "DEPRECATED: Passing a block to Capybara::server is deprecated, please use Capybara::register_server instead"
|
||||
@server = block
|
||||
else
|
||||
@server
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Set the server to use.
|
||||
#
|
||||
# Capybara.server = :webrick
|
||||
#
|
||||
# @param [Symbol] name Name of the server type to use
|
||||
# @see register_server
|
||||
#
|
||||
def server=(name)
|
||||
@server = if name.respond_to? :call
|
||||
name
|
||||
else
|
||||
servers[name.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Wraps the given string, which should contain an HTML document or fragment
|
||||
|
@ -251,29 +240,25 @@ module Capybara
|
|||
servers[:webrick].call(app, port, server_host)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# @return [Symbol] The name of the driver to use by default
|
||||
#
|
||||
def default_driver
|
||||
@default_driver || :rack_test
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# @return [Symbol] The name of the driver currently in use
|
||||
#
|
||||
def current_driver
|
||||
@current_driver || default_driver
|
||||
if threadsafe
|
||||
Thread.current['capybara_current_driver']
|
||||
else
|
||||
@current_driver
|
||||
end || default_driver
|
||||
end
|
||||
alias_method :mode, :current_driver
|
||||
|
||||
##
|
||||
#
|
||||
# @return [Symbol] The name of the driver used when JavaScript is needed
|
||||
#
|
||||
def javascript_driver
|
||||
@javascript_driver || :selenium
|
||||
def current_driver=(name)
|
||||
if threadsafe
|
||||
Thread.current['capybara_current_driver'] = name
|
||||
else
|
||||
@current_driver = name
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -281,7 +266,7 @@ module Capybara
|
|||
# Use the default driver as the current driver
|
||||
#
|
||||
def use_default_driver
|
||||
@current_driver = nil
|
||||
self.current_driver = nil
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -293,15 +278,7 @@ module Capybara
|
|||
Capybara.current_driver = driver
|
||||
yield
|
||||
ensure
|
||||
@current_driver = previous_driver
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# @return [String] The IP address bound by default server
|
||||
#
|
||||
def server_host
|
||||
@server_host || '127.0.0.1'
|
||||
self.current_driver = previous_driver
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -344,7 +321,19 @@ module Capybara
|
|||
# @return [Symbol] The name of the currently used session.
|
||||
#
|
||||
def session_name
|
||||
@session_name ||= :default
|
||||
if threadsafe
|
||||
Thread.current['capybara_session_name'] ||= :default
|
||||
else
|
||||
@session_name ||= :default
|
||||
end
|
||||
end
|
||||
|
||||
def session_name=(name)
|
||||
if threadsafe
|
||||
Thread.current['capybara_session_name'] = name
|
||||
else
|
||||
@session_name = name
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -352,11 +341,19 @@ module Capybara
|
|||
# Yield a block using a specific session name.
|
||||
#
|
||||
def using_session(name)
|
||||
previous_session_name = self.session_name
|
||||
previous_session_info = {
|
||||
session_name: session_name,
|
||||
current_driver: current_driver,
|
||||
app: app
|
||||
}
|
||||
self.session_name = name
|
||||
yield
|
||||
ensure
|
||||
self.session_name = previous_session_name
|
||||
self.session_name = previous_session_info[:session_name]
|
||||
if threadsafe
|
||||
self.current_driver = previous_session_info[:current_driver]
|
||||
self.app = previous_session_info[:app]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -374,32 +371,8 @@ module Capybara
|
|||
end
|
||||
end
|
||||
|
||||
# @deprecated Use default_max_wait_time instead
|
||||
def default_wait_time
|
||||
deprecate('default_wait_time', 'default_max_wait_time', true)
|
||||
default_max_wait_time
|
||||
end
|
||||
|
||||
# @deprecated Use default_max_wait_time= instead
|
||||
def default_wait_time=(t)
|
||||
deprecate('default_wait_time=', 'default_max_wait_time=')
|
||||
self.default_max_wait_time = t
|
||||
end
|
||||
|
||||
def save_and_open_page_path=(path)
|
||||
warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
|
||||
"Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
|
||||
@save_and_open_page_path = path
|
||||
end
|
||||
|
||||
def app_host=(url)
|
||||
raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
||||
@app_host = url
|
||||
end
|
||||
|
||||
def default_host=(url)
|
||||
raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
||||
@default_host = url
|
||||
def session_options
|
||||
config.session_options
|
||||
end
|
||||
|
||||
def included(base)
|
||||
|
@ -407,18 +380,10 @@ module Capybara
|
|||
warn "`include Capybara` is deprecated. Please use `include Capybara::DSL` instead."
|
||||
end
|
||||
|
||||
def reuse_server=(bool)
|
||||
warn "Capybara.reuse_server == false is a BETA feature and may change in a future version" unless bool
|
||||
@reuse_server = bool
|
||||
end
|
||||
|
||||
def deprecate(method, alternate_method, once=false)
|
||||
@deprecation_notified ||= {}
|
||||
warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead" unless once and @deprecation_notified[method]
|
||||
@deprecation_notified[method]=true
|
||||
end
|
||||
|
||||
private
|
||||
def config
|
||||
@config ||= Capybara::Config.new
|
||||
end
|
||||
|
||||
def session_pool
|
||||
@session_pool ||= {}
|
||||
|
|
121
lib/capybara/config.rb
Normal file
121
lib/capybara/config.rb
Normal file
|
@ -0,0 +1,121 @@
|
|||
# frozen_string_literal: true
|
||||
require 'forwardable'
|
||||
require 'capybara/session/config'
|
||||
|
||||
module Capybara
|
||||
class Config
|
||||
extend Forwardable
|
||||
|
||||
OPTIONS = [:app, :reuse_server, :threadsafe, :default_wait_time, :server, :default_driver, :javascript_driver]
|
||||
|
||||
attr_accessor :app
|
||||
attr_reader :reuse_server, :threadsafe
|
||||
attr_reader :session_options
|
||||
attr_writer :default_driver, :javascript_driver
|
||||
|
||||
SessionConfig::OPTIONS.each do |method|
|
||||
def_delegators :session_options, method, "#{method}="
|
||||
end
|
||||
|
||||
def initialize
|
||||
@session_options = Capybara::SessionConfig.new
|
||||
end
|
||||
|
||||
def reuse_server=(bool)
|
||||
@reuse_server = bool
|
||||
end
|
||||
|
||||
def threadsafe=(bool)
|
||||
warn "Capybara.threadsafe == true is a BETA feature and may change in future minor versions" if bool
|
||||
raise "Threadsafe setting cannot be changed once a session is created" if (bool != threadsafe) && Session.instance_created?
|
||||
@threadsafe = bool
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Return the proc that Capybara will call to run the Rack application.
|
||||
# The block returned receives a rack app, port, and host/ip and should run a Rack handler
|
||||
# By default, Capybara will try to run webrick.
|
||||
#
|
||||
def server(&block)
|
||||
if block_given?
|
||||
warn "DEPRECATED: Passing a block to Capybara::server is deprecated, please use Capybara::register_server instead"
|
||||
@server = block
|
||||
else
|
||||
@server
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Set the server to use.
|
||||
#
|
||||
# Capybara.server = :webrick
|
||||
#
|
||||
# @param [Symbol] name Name of the server type to use
|
||||
# @see register_server
|
||||
#
|
||||
def server=(name)
|
||||
@server = if name.respond_to? :call
|
||||
name
|
||||
else
|
||||
Capybara.servers[name.to_sym]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# @return [Symbol] The name of the driver to use by default
|
||||
#
|
||||
def default_driver
|
||||
@default_driver || :rack_test
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# @return [Symbol] The name of the driver used when JavaScript is needed
|
||||
#
|
||||
def javascript_driver
|
||||
@javascript_driver || :selenium
|
||||
end
|
||||
|
||||
# @deprecated Use default_max_wait_time instead
|
||||
def default_wait_time
|
||||
deprecate('default_wait_time', 'default_max_wait_time', true)
|
||||
default_max_wait_time
|
||||
end
|
||||
|
||||
# @deprecated Use default_max_wait_time= instead
|
||||
def default_wait_time=(t)
|
||||
deprecate('default_wait_time=', 'default_max_wait_time=')
|
||||
self.default_max_wait_time = t
|
||||
end
|
||||
|
||||
def deprecate(method, alternate_method, once=false)
|
||||
@deprecation_notified ||= {}
|
||||
warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead" unless once and @deprecation_notified[method]
|
||||
@deprecation_notified[method]=true
|
||||
end
|
||||
end
|
||||
|
||||
class ConfigureDeprecator
|
||||
def initialize(config)
|
||||
@config = config
|
||||
end
|
||||
|
||||
def method_missing(m, *args, &block)
|
||||
if @config.respond_to?(m)
|
||||
@config.public_send(m, *args, &block)
|
||||
elsif Capybara.respond_to?(m)
|
||||
warn "Calling #{m} from Capybara.configure is deprecated - please call it on Capybara directly ( Capybara.#{m}(...) )"
|
||||
Capybara.public_send(m, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to_missing?(m, include_private = false)
|
||||
@config.respond_to_missing?(m, include_private) || Capybara.respond_to_missing?(m, include_private)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
class Capybara::Driver::Base
|
||||
attr_writer :session_options
|
||||
|
||||
def current_url
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -139,6 +141,10 @@ class Capybara::Driver::Base
|
|||
false
|
||||
end
|
||||
|
||||
def session_options
|
||||
@session_options || Capybara.session_options
|
||||
end
|
||||
|
||||
# @deprecated This method is being removed
|
||||
def browser_initialized?
|
||||
warn "DEPRECATED: #browser_initialized? is deprecated and will be removed in the next version of Capybara"
|
||||
|
|
|
@ -21,12 +21,10 @@ module Capybara
|
|||
Capybara.using_session(name, &block)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Shortcut to using a different wait time.
|
||||
#
|
||||
def using_wait_time(seconds, &block)
|
||||
Capybara.using_wait_time(seconds, &block)
|
||||
page.using_wait_time(seconds, &block)
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -46,11 +46,11 @@ module Capybara
|
|||
# @param [String] html HTML code to inject into
|
||||
# @return [String] The modified HTML code
|
||||
#
|
||||
def inject_asset_host(html)
|
||||
if Capybara.asset_host && Nokogiri::HTML(html).css("base").empty?
|
||||
def inject_asset_host(html, asset_host = Capybara.asset_host)
|
||||
if asset_host && Nokogiri::HTML(html).css("base").empty?
|
||||
match = html.match(/<head[^<]*?>/)
|
||||
if match
|
||||
return html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
|
||||
return html.clone.insert match.end(0), "<base href='#{asset_host}' />"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -293,9 +293,9 @@ module Capybara
|
|||
|
||||
def _check_with_label(selector, checked, locator, options)
|
||||
locator, options = nil, locator if locator.is_a? Hash
|
||||
allow_label_click = options.delete(:allow_label_click) { Capybara.automatic_label_click }
|
||||
allow_label_click = options.delete(:allow_label_click) { session_options.automatic_label_click }
|
||||
|
||||
synchronize(Capybara::Queries::BaseQuery::wait(options)) do
|
||||
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
|
||||
begin
|
||||
el = find(selector, locator, options)
|
||||
el.set(checked)
|
||||
|
|
|
@ -74,7 +74,7 @@ module Capybara
|
|||
# @return [Object] The result of the given block
|
||||
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
|
||||
#
|
||||
def synchronize(seconds=Capybara.default_max_wait_time, options = {})
|
||||
def synchronize(seconds=session_options.default_max_wait_time, options = {})
|
||||
start_time = Capybara::Helpers.monotonic_time
|
||||
|
||||
if session.synchronized
|
||||
|
@ -90,7 +90,7 @@ module Capybara
|
|||
raise e if (Capybara::Helpers.monotonic_time - start_time) >= seconds
|
||||
sleep(0.05)
|
||||
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Capybara::Helpers.monotonic_time == start_time
|
||||
reload if Capybara.automatic_reload
|
||||
reload if session_options.automatic_reload
|
||||
retry
|
||||
ensure
|
||||
session.synchronized = false
|
||||
|
@ -114,6 +114,11 @@ module Capybara
|
|||
query_scope
|
||||
end
|
||||
|
||||
# @api private
|
||||
def session_options
|
||||
session.config
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def catch_error?(error, errors = nil)
|
||||
|
|
|
@ -55,7 +55,7 @@ module Capybara
|
|||
# @return [String] The text of the element
|
||||
#
|
||||
def text(type=nil)
|
||||
type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
|
||||
type ||= :all unless session_options.ignore_hidden_elements or session_options.visible_text_only
|
||||
synchronize do
|
||||
if type == :all
|
||||
base.all_text
|
||||
|
|
|
@ -29,11 +29,16 @@ module Capybara
|
|||
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
||||
#
|
||||
def find(*args, &optional_filter_block)
|
||||
if args.last.is_a? Hash
|
||||
args.last[:session_options] = session_options
|
||||
else
|
||||
args.push(session_options: session_options)
|
||||
end
|
||||
query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
|
||||
synchronize(query.wait) do
|
||||
if (query.match == :smart or query.match == :prefer_exact) and query.supports_exact?
|
||||
if (query.match == :smart or query.match == :prefer_exact)
|
||||
result = query.resolve_for(self, true)
|
||||
result = query.resolve_for(self, false) if result.empty? && !query.exact?
|
||||
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
|
||||
else
|
||||
result = query.resolve_for(self)
|
||||
end
|
||||
|
@ -208,6 +213,11 @@ module Capybara
|
|||
# @return [Capybara::Result] A collection of found elements
|
||||
#
|
||||
def all(*args, &optional_filter_block)
|
||||
if args.last.is_a? Hash
|
||||
args.last[:session_options] = session_options
|
||||
else
|
||||
args.push(session_options: session_options)
|
||||
end
|
||||
query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
|
||||
synchronize(query.wait) do
|
||||
result = query.resolve_for(self)
|
||||
|
@ -233,7 +243,7 @@ module Capybara
|
|||
# @return [Capybara::Node::Element] The found element or nil
|
||||
#
|
||||
def first(*args, &optional_filter_block)
|
||||
if Capybara.wait_on_first_by_default
|
||||
if session_options.wait_on_first_by_default
|
||||
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
args.push({minimum: 1}.merge(options))
|
||||
end
|
||||
|
|
|
@ -114,8 +114,8 @@ module Capybara
|
|||
#
|
||||
def assert_all_of_selectors(*args, &optional_filter_block)
|
||||
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
selector = if args.first.is_a?(Symbol) then args.shift else Capybara.default_selector end
|
||||
wait = options.fetch(:wait, Capybara.default_max_wait_time)
|
||||
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
||||
wait = options.fetch(:wait, session_options.default_max_wait_time)
|
||||
synchronize(wait) do
|
||||
args.each do |locator|
|
||||
assert_selector(selector, locator, options, &optional_filter_block)
|
||||
|
@ -140,8 +140,8 @@ module Capybara
|
|||
#
|
||||
def assert_none_of_selectors(*args, &optional_filter_block)
|
||||
options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
selector = if args.first.is_a?(Symbol) then args.shift else Capybara.default_selector end
|
||||
wait = options.fetch(:wait, Capybara.default_max_wait_time)
|
||||
selector = if args.first.is_a?(Symbol) then args.shift else session_options.default_selector end
|
||||
wait = options.fetch(:wait, session_options.default_max_wait_time)
|
||||
synchronize(wait) do
|
||||
args.each do |locator|
|
||||
assert_no_selector(selector, locator, options, &optional_filter_block)
|
||||
|
@ -680,6 +680,7 @@ module Capybara
|
|||
private
|
||||
|
||||
def _verify_selector_result(query_args, optional_filter_block, &result_block)
|
||||
_set_query_session_options(query_args)
|
||||
query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
|
||||
synchronize(query.wait) do
|
||||
result = query.resolve_for(self)
|
||||
|
@ -689,6 +690,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def _verify_match_result(query_args, optional_filter_block, &result_block)
|
||||
_set_query_session_options(query_args)
|
||||
query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
|
||||
synchronize(query.wait) do
|
||||
result = query.resolve_for(self.query_scope)
|
||||
|
@ -698,6 +700,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def _verify_text(query_args)
|
||||
_set_query_session_options(query_args)
|
||||
query = Capybara::Queries::TextQuery.new(*query_args)
|
||||
synchronize(query.wait) do
|
||||
count = query.resolve_for(self)
|
||||
|
@ -706,6 +709,14 @@ module Capybara
|
|||
return true
|
||||
end
|
||||
|
||||
def _set_query_session_options(query_args)
|
||||
if query_args.last.is_a? Hash
|
||||
query_args.last[:session_options] = session_options
|
||||
else
|
||||
query_args.push(session_options: session_options)
|
||||
end
|
||||
query_args
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -173,6 +173,11 @@ module Capybara
|
|||
def find_xpath(xpath)
|
||||
native.xpath(xpath)
|
||||
end
|
||||
|
||||
# @api private
|
||||
def session_options
|
||||
Capybara.session_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,13 +6,18 @@ module Capybara
|
|||
COUNT_KEYS = [:count, :minimum, :maximum, :between]
|
||||
|
||||
attr_reader :options
|
||||
attr_writer :session_options
|
||||
|
||||
def wait
|
||||
self.class.wait(options)
|
||||
def session_options
|
||||
@session_options || Capybara.session_options
|
||||
end
|
||||
|
||||
def self.wait(options)
|
||||
options.fetch(:wait, Capybara.default_max_wait_time) || 0
|
||||
def wait
|
||||
self.class.wait(options, session_options.default_max_wait_time)
|
||||
end
|
||||
|
||||
def self.wait(options, default=Capybara.default_max_wait_time)
|
||||
options.fetch(:wait, default) || 0
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -9,11 +9,13 @@ module Capybara
|
|||
|
||||
def initialize(*args, &filter_block)
|
||||
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
self.session_options = @options.delete(:session_options)
|
||||
|
||||
@filter_block = filter_block
|
||||
|
||||
if args[0].is_a?(Symbol)
|
||||
@selector = Selector.all.fetch(args.shift) do |selector_type|
|
||||
warn "Unknown selector type (:#{selector_type}), defaulting to :#{Capybara.default_selector} - This will raise an exception in a future version of Capybara"
|
||||
raise ArgumentError, "Unknown selector type (:#{selector_type})"
|
||||
nil
|
||||
end
|
||||
@locator = args.shift
|
||||
|
@ -21,16 +23,16 @@ module Capybara
|
|||
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
|
||||
@locator = args.shift
|
||||
end
|
||||
@selector ||= Selector.all[Capybara.default_selector]
|
||||
@selector ||= Selector.all[session_options.default_selector]
|
||||
|
||||
warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
|
||||
|
||||
# for compatibility with Capybara 2.0
|
||||
if Capybara.exact_options and @selector == Selector.all[:option]
|
||||
if session_options.exact_options and @selector == Selector.all[:option]
|
||||
@options[:exact] = true
|
||||
end
|
||||
|
||||
@expression = @selector.call(@locator, @options)
|
||||
@expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
|
||||
|
||||
warn_exact_usage
|
||||
|
||||
|
@ -89,12 +91,12 @@ module Capybara
|
|||
end
|
||||
end
|
||||
|
||||
res &&= Capybara.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
|
||||
res &&= node.session.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
|
||||
res
|
||||
end
|
||||
|
||||
def visible
|
||||
case (vis = options.fetch(:visible){ @selector.default_visibility })
|
||||
case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
|
||||
when true then :visible
|
||||
when false then :all
|
||||
else vis
|
||||
|
@ -103,11 +105,11 @@ module Capybara
|
|||
|
||||
def exact?
|
||||
return false if !supports_exact?
|
||||
options.fetch(:exact, Capybara.exact)
|
||||
options.fetch(:exact, session_options.exact)
|
||||
end
|
||||
|
||||
def match
|
||||
options.fetch(:match, Capybara.match)
|
||||
options.fetch(:match, session_options.match)
|
||||
end
|
||||
|
||||
def xpath(exact=nil)
|
||||
|
@ -205,7 +207,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def exact_text
|
||||
options.fetch(:exact_text, Capybara.exact_text)
|
||||
options.fetch(:exact_text, session_options.exact_text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,13 +5,18 @@ module Capybara
|
|||
class TextQuery < BaseQuery
|
||||
def initialize(*args)
|
||||
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
||||
@type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
||||
@expected_text, @options = args
|
||||
# @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
||||
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
||||
self.session_options = @options.delete(:session_options)
|
||||
|
||||
@type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
|
||||
|
||||
@expected_text = args.shift
|
||||
unless @expected_text.is_a?(Regexp)
|
||||
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
||||
end
|
||||
@options ||= {}
|
||||
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
||||
warn "Unused parameters passed to #{self.class.name} : #{args.to_s}" unless args.empty?
|
||||
assert_valid_keys
|
||||
end
|
||||
|
||||
|
@ -40,7 +45,7 @@ module Capybara
|
|||
private
|
||||
|
||||
def exact?
|
||||
options.fetch(:exact, Capybara.exact_text)
|
||||
options.fetch(:exact, session_options.exact_text)
|
||||
end
|
||||
|
||||
def build_message(report_on_invisible)
|
||||
|
|
|
@ -71,7 +71,7 @@ class Capybara::RackTest::Browser
|
|||
end
|
||||
|
||||
def reset_host!
|
||||
uri = URI.parse(Capybara.app_host || Capybara.default_host)
|
||||
uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
|
||||
@current_scheme = uri.scheme
|
||||
@current_host = uri.host
|
||||
@current_port = uri.port
|
||||
|
|
|
@ -7,7 +7,7 @@ module Capybara
|
|||
attr_reader :failure_message, :failure_message_when_negated
|
||||
|
||||
def wrap(actual)
|
||||
if actual.respond_to?("has_selector?")
|
||||
@context_el = if actual.respond_to?("has_selector?")
|
||||
actual
|
||||
else
|
||||
Capybara.string(actual.to_s)
|
||||
|
@ -33,6 +33,19 @@ module Capybara
|
|||
@failure_message_when_negated = e.message
|
||||
return false
|
||||
end
|
||||
|
||||
def session_query_args
|
||||
if @args.last.is_a? Hash
|
||||
@args.last[:session_options] = session_options
|
||||
else
|
||||
@args.push(session_options: session_options)
|
||||
end
|
||||
@args
|
||||
end
|
||||
|
||||
def session_options
|
||||
@context_el ? @context_el.session_options : Capybara.session_options
|
||||
end
|
||||
end
|
||||
|
||||
class HaveSelector < Matcher
|
||||
|
@ -55,7 +68,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def query
|
||||
@query ||= Capybara::Queries::SelectorQuery.new(*@args, &@filter_block)
|
||||
@query ||= Capybara::Queries::SelectorQuery.new(*session_query_args, &@filter_block)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,7 +86,7 @@ module Capybara
|
|||
end
|
||||
|
||||
def query
|
||||
@query ||= Capybara::Queries::MatchQuery.new(*@args)
|
||||
@query ||= Capybara::Queries::MatchQuery.new(*session_query_args, &@filter_block)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -155,11 +168,12 @@ module Capybara
|
|||
|
||||
class BecomeClosed
|
||||
def initialize(options)
|
||||
@wait_time = Capybara::Queries::BaseQuery.wait(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def matches?(window)
|
||||
@window = window
|
||||
@wait_time = Capybara::Queries::BaseQuery.wait(@options, window.session.config.default_max_wait_time)
|
||||
start_time = Capybara::Helpers.monotonic_time
|
||||
while window.exists?
|
||||
return false if (Capybara::Helpers.monotonic_time - start_time) > @wait_time
|
||||
|
|
|
@ -139,7 +139,7 @@ Capybara.add_selector(:link) do
|
|||
XPath.string.n.is(locator) |
|
||||
XPath.attr(:title).is(locator) |
|
||||
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
||||
matchers |= XPath.attr(:'aria-label').is(locator) if Capybara.enable_aria_label
|
||||
matchers |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
xpath = xpath[matchers]
|
||||
end
|
||||
xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
||||
|
@ -185,14 +185,14 @@ Capybara.add_selector(:button) do
|
|||
unless locator.nil?
|
||||
locator = locator.to_s
|
||||
locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
||||
locator_matches |= XPath.attr(:'aria-label').is(locator) if Capybara.enable_aria_label
|
||||
locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
|
||||
input_btn_xpath = input_btn_xpath[locator_matches]
|
||||
|
||||
btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
||||
|
||||
alt_matches = XPath.attr(:alt).is(locator)
|
||||
alt_matches |= XPath.attr(:'aria-label').is(locator) if Capybara.enable_aria_label
|
||||
alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
image_btn_xpath = image_btn_xpath[alt_matches]
|
||||
end
|
||||
|
||||
|
|
|
@ -201,9 +201,9 @@ module Capybara
|
|||
@default_visibility = default_visibility
|
||||
end
|
||||
|
||||
def default_visibility
|
||||
def default_visibility(fallback = Capybara.ignore_hidden_elements)
|
||||
if @default_visibility.nil?
|
||||
Capybara.ignore_hidden_elements
|
||||
fallback
|
||||
else
|
||||
@default_visibility
|
||||
end
|
||||
|
@ -219,7 +219,7 @@ module Capybara
|
|||
XPath.attr(:name).equals(locator) |
|
||||
XPath.attr(:placeholder).equals(locator) |
|
||||
XPath.attr(:id).equals(XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for))
|
||||
attr_matchers |= XPath.attr(:'aria-label').is(locator) if Capybara.enable_aria_label
|
||||
attr_matchers |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
||||
|
||||
locate_xpath = locate_xpath[attr_matchers]
|
||||
locate_xpath += XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
|
||||
|
|
|
@ -320,7 +320,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|||
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
|
||||
# Actual wait time may be longer than specified
|
||||
wait = Selenium::WebDriver::Wait.new(
|
||||
timeout: (options[:wait] || Capybara.default_max_wait_time),
|
||||
timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0 ,
|
||||
ignore: Selenium::WebDriver::Error::NoAlertPresentError)
|
||||
begin
|
||||
wait.until do
|
||||
|
|
|
@ -25,9 +25,10 @@ module Capybara
|
|||
|
||||
attr_accessor :error
|
||||
|
||||
def initialize(app)
|
||||
def initialize(app, server_errors)
|
||||
@app = app
|
||||
@counter = Counter.new
|
||||
@server_errors = server_errors
|
||||
end
|
||||
|
||||
def pending_requests?
|
||||
|
@ -41,7 +42,7 @@ module Capybara
|
|||
@counter.increment
|
||||
begin
|
||||
@app.call(env)
|
||||
rescue *Capybara.server_errors => e
|
||||
rescue *@server_errors => e
|
||||
@error = e unless @error
|
||||
raise e
|
||||
ensure
|
||||
|
@ -59,10 +60,10 @@ module Capybara
|
|||
|
||||
attr_reader :app, :port, :host
|
||||
|
||||
def initialize(app, port=Capybara.server_port, host=Capybara.server_host)
|
||||
def initialize(app, port=Capybara.server_port, host=Capybara.server_host, server_errors=Capybara.server_errors)
|
||||
@app = app
|
||||
@server_thread = nil # suppress warnings
|
||||
@host, @port = host, port
|
||||
@host, @port, @server_errors = host, port, server_errors
|
||||
@port ||= Capybara::Server.ports[port_key]
|
||||
@port ||= find_available_port(host)
|
||||
end
|
||||
|
@ -112,7 +113,7 @@ module Capybara
|
|||
private
|
||||
|
||||
def middleware
|
||||
@middleware ||= Middleware.new(app)
|
||||
@middleware ||= Middleware.new(app, @server_errors)
|
||||
end
|
||||
|
||||
def port_key
|
||||
|
|
|
@ -17,6 +17,14 @@ module Capybara
|
|||
# session = Capybara::Session.new(:culerity)
|
||||
# session.visit('http://www.google.com')
|
||||
#
|
||||
# When Capybara.threadsafe == true the sessions options will be initially set to the
|
||||
# current values of the global options and a configuration block can be passed to the session initializer.
|
||||
# For available options see {Capybara::SessionConfig::OPTIONS}
|
||||
#
|
||||
# session = Capybara::Session.new(:driver, MyRackApp) do |config|
|
||||
# config.app_host = "http://my_host.dev"
|
||||
# end
|
||||
#
|
||||
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
|
||||
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
|
||||
# the current HTML document. This allows interaction:
|
||||
|
@ -69,10 +77,15 @@ module Capybara
|
|||
|
||||
def initialize(mode, app=nil)
|
||||
raise TypeError, "The second parameter to Session::new should be a rack app if passed." if app && !app.respond_to?(:call)
|
||||
@@instance_created = true
|
||||
@mode = mode
|
||||
@app = app
|
||||
if Capybara.run_server and @app and driver.needs_server?
|
||||
@server = Capybara::Server.new(@app).boot
|
||||
if block_given?
|
||||
raise "A configuration block is only accepted when Capybara.threadsafe == true" unless Capybara.threadsafe
|
||||
yield config if block_given?
|
||||
end
|
||||
if config.run_server and @app and driver.needs_server?
|
||||
@server = Capybara::Server.new(@app, config.server_port, config.server_host, config.server_errors).boot
|
||||
else
|
||||
@server = nil
|
||||
end
|
||||
|
@ -85,7 +98,9 @@ module Capybara
|
|||
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
||||
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
||||
end
|
||||
Capybara.drivers[mode].call(app)
|
||||
driver = Capybara.drivers[mode].call(app)
|
||||
driver.session_options = config
|
||||
driver
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -126,7 +141,7 @@ module Capybara
|
|||
if @server and @server.error
|
||||
# Force an explanation for the error being raised as the exception cause
|
||||
begin
|
||||
if Capybara.raise_server_errors
|
||||
if config.raise_server_errors
|
||||
raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
|
||||
end
|
||||
rescue CapybaraError
|
||||
|
@ -235,10 +250,10 @@ module Capybara
|
|||
visit_uri = URI.parse(visit_uri.to_s)
|
||||
|
||||
uri_base = if @server
|
||||
visit_uri.port = @server.port if Capybara.always_include_port && (visit_uri.port == visit_uri.default_port)
|
||||
URI.parse(Capybara.app_host || "http://#{@server.host}:#{@server.port}")
|
||||
visit_uri.port = @server.port if config.always_include_port && (visit_uri.port == visit_uri.default_port)
|
||||
URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
|
||||
else
|
||||
Capybara.app_host && URI.parse(Capybara.app_host)
|
||||
config.app_host && URI.parse(config.app_host)
|
||||
end
|
||||
|
||||
# TODO - this is only for compatability with previous 2.x behavior that concatenated
|
||||
|
@ -481,7 +496,7 @@ module Capybara
|
|||
driver.switch_to_window(window.handle)
|
||||
window
|
||||
else
|
||||
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
||||
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
||||
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
||||
original_window_handle = driver.current_window_handle
|
||||
begin
|
||||
|
@ -578,7 +593,7 @@ module Capybara
|
|||
old_handles = driver.window_handles
|
||||
block.call
|
||||
|
||||
wait_time = Capybara::Queries::BaseQuery.wait(options)
|
||||
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
||||
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
||||
opened_handles = (driver.window_handles - old_handles)
|
||||
if opened_handles.size != 1
|
||||
|
@ -701,7 +716,7 @@ module Capybara
|
|||
#
|
||||
def save_page(path = nil)
|
||||
path = prepare_path(path, 'html')
|
||||
File.write(path, Capybara::Helpers.inject_asset_host(body), mode: 'wb')
|
||||
File.write(path, Capybara::Helpers.inject_asset_host(body, config.asset_host), mode: 'wb')
|
||||
path
|
||||
end
|
||||
|
||||
|
@ -786,7 +801,49 @@ module Capybara
|
|||
scope
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Yield a block using a specific wait time
|
||||
#
|
||||
def using_wait_time(seconds)
|
||||
if Capybara.threadsafe
|
||||
begin
|
||||
previous_wait_time = config.default_max_wait_time
|
||||
config.default_max_wait_time = seconds
|
||||
yield
|
||||
ensure
|
||||
config.default_max_wait_time = previous_wait_time
|
||||
end
|
||||
else
|
||||
Capybara.using_wait_time(seconds) { yield }
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Accepts a block to set the configuration options if Capybara.threadsafe == true. Note that some options only have an effect
|
||||
# if set at initialization time, so look at the configuration block that can be passed to the initializer too
|
||||
#
|
||||
def configure
|
||||
raise "Session configuration is only supported when Capybara.threadsafe == true" unless Capybara.threadsafe
|
||||
yield config
|
||||
end
|
||||
|
||||
def self.instance_created?
|
||||
@@instance_created
|
||||
end
|
||||
|
||||
def config
|
||||
@config ||= if Capybara.threadsafe
|
||||
Capybara.session_options.dup
|
||||
else
|
||||
Capybara::ReadOnlySessionConfig.new(Capybara.session_options)
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
@@instance_created = false
|
||||
|
||||
def accept_modal(type, text_or_options, options, &blk)
|
||||
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
||||
end
|
||||
|
@ -798,7 +855,7 @@ module Capybara
|
|||
def modal_options(text_or_options, options)
|
||||
text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
|
||||
options[:text] ||= text_or_options unless text_or_options.nil?
|
||||
options[:wait] ||= Capybara.default_max_wait_time
|
||||
options[:wait] ||= config.default_max_wait_time
|
||||
options
|
||||
end
|
||||
|
||||
|
@ -814,10 +871,10 @@ module Capybara
|
|||
end
|
||||
|
||||
def prepare_path(path, extension)
|
||||
if Capybara.save_path || Capybara.save_and_open_page_path.nil?
|
||||
path = File.expand_path(path || default_fn(extension), Capybara.save_path)
|
||||
if config.save_path || config.save_and_open_page_path.nil?
|
||||
path = File.expand_path(path || default_fn(extension), config.save_path)
|
||||
else
|
||||
path = File.expand_path(default_fn(extension), Capybara.save_and_open_page_path) if path.nil?
|
||||
path = File.expand_path(default_fn(extension), config.save_and_open_page_path) if path.nil?
|
||||
end
|
||||
FileUtils.mkdir_p(File.dirname(path))
|
||||
path
|
||||
|
|
100
lib/capybara/session/config.rb
Normal file
100
lib/capybara/session/config.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
# frozen_string_literal: true
|
||||
require 'delegate'
|
||||
|
||||
module Capybara
|
||||
class SessionConfig
|
||||
OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
|
||||
:automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
|
||||
:automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
|
||||
:save_and_open_page_path, :server_host, :server_port, :server_errors]
|
||||
|
||||
attr_accessor *OPTIONS
|
||||
|
||||
##
|
||||
#@!method always_include_port
|
||||
# See {Capybara#configure}
|
||||
#@!method run_server
|
||||
# See {Capybara#configure}
|
||||
#@!method default_selector
|
||||
# See {Capybara#configure}
|
||||
#@!method default_max_wait_time
|
||||
# See {Capybara#configure}
|
||||
#@!method ignore_hidden_elements
|
||||
# See {Capybara#configure}
|
||||
#@!method automatic_reload
|
||||
# See {Capybara#configure}
|
||||
#@!method match
|
||||
# See {Capybara#configure}
|
||||
#@!method exact
|
||||
# See {Capybara#configure}
|
||||
#@!method raise_server_errors
|
||||
# See {Capybara#configure}
|
||||
#@!method visible_text_only
|
||||
# See {Capybara#configure}
|
||||
#@!method wait_on_first_by_default
|
||||
# See {Capybara#configure}
|
||||
#@!method automatic_label_click
|
||||
# See {Capybara#configure}
|
||||
#@!method enable_aria_label
|
||||
# See {Capybara#configure}
|
||||
#@!method save_path
|
||||
# See {Capybara#configure}
|
||||
#@!method exact_options
|
||||
# See {Capybara#configure}
|
||||
#@!method asset_host
|
||||
# See {Capybara#configure}
|
||||
#@!method default_host
|
||||
# See {Capybara#configure}
|
||||
#@!method app_host
|
||||
# See {Capybara#configure}
|
||||
#@!method save_and_open_page_path
|
||||
# See {Capybara#configure}
|
||||
#@!method server_host
|
||||
# See {Capybara#configure}
|
||||
#@!method server_port
|
||||
# See {Capybara#configure}
|
||||
#@!method server_errors
|
||||
# See {Capybara#configure}
|
||||
|
||||
##
|
||||
#
|
||||
# @return [String] The IP address bound by default server
|
||||
#
|
||||
def server_host
|
||||
@server_host || '127.0.0.1'
|
||||
end
|
||||
|
||||
def server_errors=(errors)
|
||||
(@server_errors ||= []).replace(errors.dup)
|
||||
end
|
||||
|
||||
def app_host=(url)
|
||||
raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
||||
@app_host = url
|
||||
end
|
||||
|
||||
def default_host=(url)
|
||||
raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
|
||||
@default_host = url
|
||||
end
|
||||
|
||||
def save_and_open_page_path=(path)
|
||||
warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
|
||||
"Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
|
||||
@save_and_open_page_path = path
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
super
|
||||
@server_errors = @server_errors.dup
|
||||
end
|
||||
end
|
||||
|
||||
class ReadOnlySessionConfig < SimpleDelegator
|
||||
SessionConfig::OPTIONS.each do |m|
|
||||
define_method "#{m}=" do |val|
|
||||
raise "Per session settings are only supported when Capybara.threadsafe == true"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -58,7 +58,7 @@ $(function() {
|
|||
$('#change-title').click(function() {
|
||||
setTimeout(function() {
|
||||
$('title').text('changed title')
|
||||
}, 250)
|
||||
}, 400)
|
||||
});
|
||||
$('#click-test').on({
|
||||
dblclick: function() {
|
||||
|
|
|
@ -66,6 +66,17 @@ Capybara::SpecHelper.spec "#all" do
|
|||
Capybara.ignore_hidden_elements = false
|
||||
expect(@session.all(:css, "a.simple").size).to eq(2)
|
||||
end
|
||||
|
||||
context "with per session config", requires: [:psc] do
|
||||
it "should use the sessions ignore_hidden_elements", psc: true do
|
||||
Capybara.ignore_hidden_elements = true
|
||||
@session.config.ignore_hidden_elements = false
|
||||
expect(Capybara.ignore_hidden_elements).to eq(true)
|
||||
expect(@session.all(:css, "a.simple").size).to eq(2)
|
||||
@session.config.ignore_hidden_elements = true
|
||||
expect(@session.all(:css, "a.simple").size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with element count filters' do
|
||||
|
|
|
@ -18,10 +18,18 @@ Capybara::SpecHelper.spec '#assert_all_of_selectors' do
|
|||
@session.assert_all_of_selectors("p a#foo", "h2#h2two", "h2#h2one" )
|
||||
end
|
||||
|
||||
it "should respect scopes" do
|
||||
@session.within "//p[@id='first']" do
|
||||
@session.assert_all_of_selectors(".//a[@id='foo']")
|
||||
expect { @session.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
|
||||
context "should respect scopes" do
|
||||
it "when used with `within`" do
|
||||
@session.within "//p[@id='first']" do
|
||||
@session.assert_all_of_selectors(".//a[@id='foo']")
|
||||
expect { @session.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
it "when called on elements" do
|
||||
el = @session.find "//p[@id='first']"
|
||||
el.assert_all_of_selectors(".//a[@id='foo']")
|
||||
expect { el.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,10 +73,18 @@ Capybara::SpecHelper.spec '#assert_none_of_selectors' do
|
|||
expect { @session.assert_none_of_selectors("abbr", "p a#foo") }.to raise_error(Capybara::ElementNotFound)
|
||||
end
|
||||
|
||||
it "should respect scopes" do
|
||||
@session.within "//p[@id='first']" do
|
||||
expect { @session.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
|
||||
@session.assert_none_of_selectors(".//a[@id='red']")
|
||||
context "should respect scopes" do
|
||||
it "when used with `within`" do
|
||||
@session.within "//p[@id='first']" do
|
||||
expect { @session.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
|
||||
@session.assert_none_of_selectors(".//a[@id='red']")
|
||||
end
|
||||
end
|
||||
|
||||
it "when called on an element" do
|
||||
el = @session.find "//p[@id='first']"
|
||||
expect { el.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
|
||||
el.assert_none_of_selectors(".//a[@id='red']")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -421,8 +421,9 @@ Capybara::SpecHelper.spec '#find' do
|
|||
end
|
||||
end
|
||||
|
||||
it "should warn if selector type is unknown" do
|
||||
expect_any_instance_of(Kernel).to receive(:warn).with(/^Unknown selector type/)
|
||||
@session.find(:unknown, '//h1')
|
||||
it "should raise if selector type is unknown" do
|
||||
expect do
|
||||
@session.find(:unknown, '//h1')
|
||||
end.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#reset_session!' do
|
|||
end
|
||||
|
||||
it "raises configured errors caught inside the server", requires: [:server] do
|
||||
prev_errors = Capybara.server_errors
|
||||
prev_errors = Capybara.server_errors.dup
|
||||
|
||||
Capybara.server_errors = [LoadError]
|
||||
quietly { @session.visit("/error") }
|
||||
|
|
|
@ -60,18 +60,18 @@ Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
|
|||
end
|
||||
|
||||
context 'with not_to' do
|
||||
it 'should raise error if default_max_wait_time is more than timeout' do
|
||||
it "should not raise error if window doesn't close before default_max_wait_time" do
|
||||
@session.within_window @other_window do
|
||||
@session.execute_script('setTimeout(function(){ window.close(); }, 700);')
|
||||
@session.execute_script('setTimeout(function(){ window.close(); }, 1000);')
|
||||
end
|
||||
Capybara.using_wait_time 0.4 do
|
||||
Capybara.using_wait_time 0.3 do
|
||||
expect do
|
||||
expect(@other_window).not_to become_closed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'should raise error if default_max_wait_time is more than timeout' do
|
||||
it 'should raise error if window closes before default_max_wait_time' do
|
||||
@session.within_window @other_window do
|
||||
@session.execute_script('setTimeout(function(){ window.close(); }, 700);')
|
||||
end
|
||||
|
|
|
@ -39,6 +39,7 @@ module Capybara
|
|||
Capybara.match = :smart
|
||||
Capybara.wait_on_first_by_default = false
|
||||
Capybara.enable_aria_label = false
|
||||
reset_threadsafe
|
||||
end
|
||||
|
||||
def filter(requires, metadata)
|
||||
|
@ -64,9 +65,19 @@ module Capybara
|
|||
before do
|
||||
@session = session
|
||||
end
|
||||
|
||||
after do
|
||||
@session.reset_session!
|
||||
end
|
||||
|
||||
before :each, psc: true do
|
||||
SpecHelper.reset_threadsafe(true, @session)
|
||||
end
|
||||
|
||||
after psc: true do
|
||||
SpecHelper.reset_threadsafe(false, @session)
|
||||
end
|
||||
|
||||
specs.each do |spec_name, spec_options, block|
|
||||
describe spec_name, spec_options do
|
||||
class_eval(&block)
|
||||
|
@ -74,6 +85,13 @@ module Capybara
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def reset_threadsafe(bool = false, session = nil)
|
||||
Capybara::Session.class_variable_set(:@@instance_created, false) # Work around limit on when threadsafe can be changed
|
||||
Capybara.threadsafe = bool
|
||||
session = session.current_session if session.respond_to?(:current_session)
|
||||
session.instance_variable_set(:@config, nil) if session
|
||||
end
|
||||
end # class << self
|
||||
|
||||
def silence_stream(stream)
|
||||
|
|
|
@ -115,7 +115,7 @@ module Capybara
|
|||
|
||||
private
|
||||
|
||||
def wait_for_stable_size(seconds=Capybara.default_max_wait_time)
|
||||
def wait_for_stable_size(seconds=session.config.default_max_wait_time)
|
||||
res = yield if block_given?
|
||||
prev_size = size
|
||||
start_time = Capybara::Helpers.monotonic_time
|
||||
|
|
|
@ -14,8 +14,8 @@ RSpec.describe Capybara do
|
|||
end
|
||||
|
||||
it "should be accesible as the deprecated default_wait_time" do
|
||||
expect(Capybara).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time= is deprecated, please use #default_max_wait_time= instead')
|
||||
expect(Capybara).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time is deprecated, please use #default_max_wait_time instead')
|
||||
expect(Capybara.send(:config)).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time= is deprecated, please use #default_max_wait_time= instead')
|
||||
expect(Capybara.send(:config)).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time is deprecated, please use #default_max_wait_time instead')
|
||||
@previous_default_time = Capybara.default_max_wait_time
|
||||
Capybara.default_wait_time = 5
|
||||
expect(Capybara.default_wait_time).to eq(5)
|
||||
|
@ -114,6 +114,18 @@ RSpec.describe Capybara do
|
|||
expect { Capybara.default_host = "http://www.example.com" }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "configure" do
|
||||
it 'deprecates calling non configuration option methods in configure' do
|
||||
expect_any_instance_of(Kernel).to receive(:warn).
|
||||
with('Calling register_driver from Capybara.configure is deprecated - please call it on Capybara directly ( Capybara.register_driver(...) )')
|
||||
Capybara.configure do |config|
|
||||
config.register_driver(:random_name) do
|
||||
#just a random block
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.describe Capybara::Session do
|
||||
|
|
|
@ -16,6 +16,7 @@ Capybara::SpecHelper.run_specs TestClass.new, "DSL", capybara_skip: [
|
|||
:server,
|
||||
:hover,
|
||||
:about_scheme,
|
||||
:psc
|
||||
]
|
||||
|
||||
RSpec.describe Capybara::DSL do
|
||||
|
|
67
spec/per_session_config_spec.rb
Normal file
67
spec/per_session_config_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
require 'capybara/dsl'
|
||||
|
||||
RSpec.describe Capybara::SessionConfig do
|
||||
describe "threadsafe" do
|
||||
it "defaults to global session options" do
|
||||
Capybara.threadsafe = true
|
||||
session = Capybara::Session.new(:rack_test, TestApp)
|
||||
[:default_host, :app_host, :save_and_open_page_path,
|
||||
:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
|
||||
:automatic_reload, :match, :exact, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
|
||||
:automatic_label_click, :enable_aria_label,
|
||||
:save_path, :exact_options, :asset_host].each do |m|
|
||||
expect(session.config.public_send(m)).to eq Capybara.public_send(m)
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't change global session when changed" do
|
||||
Capybara.threadsafe = true
|
||||
host = "http://my.example.com"
|
||||
session = Capybara::Session.new(:rack_test, TestApp) do |config|
|
||||
config.default_host = host
|
||||
config.automatic_label_click = !config.automatic_label_click
|
||||
config.server_errors << ArgumentError
|
||||
end
|
||||
expect(Capybara.default_host).not_to eq host
|
||||
expect(session.config.default_host).to eq host
|
||||
expect(Capybara.automatic_label_click).not_to eq session.config.automatic_label_click
|
||||
expect(Capybara.server_errors).not_to eq session.config.server_errors
|
||||
end
|
||||
|
||||
it "doesn't allow session configuration block when false" do
|
||||
Capybara.threadsafe = false
|
||||
expect do
|
||||
Capybara::Session.new(:rack_test, TestApp) { |config| }
|
||||
end.to raise_error "A configuration block is only accepted when Capybara.threadsafe == true"
|
||||
end
|
||||
|
||||
it "doesn't allow session config when false" do
|
||||
Capybara.threadsafe = false
|
||||
session = Capybara::Session.new(:rack_test, TestApp)
|
||||
expect { session.config.default_selector = :title }.to raise_error /Per session settings are only supported when Capybara.threadsafe == true/
|
||||
expect do
|
||||
session.configure do |config|
|
||||
config.exact = true
|
||||
end
|
||||
end.to raise_error /Session configuration is only supported when Capybara.threadsafe == true/
|
||||
end
|
||||
|
||||
it "uses the config from the session" do
|
||||
Capybara.threadsafe = true
|
||||
session = Capybara::Session.new(:rack_test, TestApp) do |config|
|
||||
config.default_selector = :link
|
||||
end
|
||||
session.visit('/with_html')
|
||||
expect(session.find('foo').tag_name).to eq 'a'
|
||||
end
|
||||
|
||||
it "won't change threadsafe once a session is created" do
|
||||
Capybara.threadsafe = true
|
||||
Capybara.threadsafe = false
|
||||
session = Capybara::Session.new(:rack_test, TestApp)
|
||||
expect { Capybara.threadsafe = true }.to raise_error /Threadsafe setting cannot be changed once a session is created/
|
||||
end
|
||||
end
|
||||
end
|
|
@ -555,8 +555,8 @@ RSpec.shared_examples Capybara::RSpecMatchers do |session, mode|
|
|||
|
||||
it "doesn't wait if wait time is less than timeout" do
|
||||
@session.click_link("Change title")
|
||||
using_wait_time 0 do
|
||||
expect(@session).not_to have_title('changed title')
|
||||
using_wait_time 3 do
|
||||
expect(@session).not_to have_title('changed title', wait: 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,4 +7,48 @@ RSpec.describe Capybara::Session do
|
|||
Capybara::Session.new(:unknown, { random: "hash"})
|
||||
end.to raise_error TypeError, "The second parameter to Session::new should be a rack app if passed."
|
||||
end
|
||||
|
||||
context "current_driver" do
|
||||
it "is global when threadsafe false" do
|
||||
Capybara.threadsafe = false
|
||||
Capybara.current_driver = :selenium
|
||||
thread = Thread.new do
|
||||
Capybara.current_driver = :random
|
||||
end
|
||||
thread.join
|
||||
expect(Capybara.current_driver).to eq :random
|
||||
end
|
||||
|
||||
it "is thread specific threadsafe true" do
|
||||
Capybara.threadsafe = true
|
||||
Capybara.current_driver = :selenium
|
||||
thread = Thread.new do
|
||||
Capybara.current_driver = :random
|
||||
end
|
||||
thread.join
|
||||
expect(Capybara.current_driver).to eq :selenium
|
||||
end
|
||||
end
|
||||
|
||||
context "session_name" do
|
||||
it "is global when threadsafe false" do
|
||||
Capybara.threadsafe = false
|
||||
Capybara.session_name = "sess1"
|
||||
thread = Thread.new do
|
||||
Capybara.session_name = "sess2"
|
||||
end
|
||||
thread.join
|
||||
expect(Capybara.session_name).to eq "sess2"
|
||||
end
|
||||
|
||||
it "is thread specific when threadsafe true" do
|
||||
Capybara.threadsafe = true
|
||||
Capybara.session_name = "sess1"
|
||||
thread = Thread.new do
|
||||
Capybara.session_name = "sess2"
|
||||
end
|
||||
thread.join
|
||||
expect(Capybara.session_name).to eq "sess1"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue