Source code for capybara
from __future__ import absolute_import
from contextlib import contextmanager
from capybara.version import __version__
app = None
""" object: The WSGI-compliant app to test. """
app_host = None
""" str: The default host to use when giving a relative URL to visit. Must be a valid URL. """
default_driver = "werkzeug"
""" str: The name of the driver default driver to use. """
javascript_driver = "selenium"
""" str: The name of the driver to use when JavaScript is required. """
current_driver = None
""" str: The name of the driver currently in use. """
server_name = "default"
""" str: The name of the server to use to serve the app. """
server_host = "127.0.0.1"
""" str: The IP address bound by the default server. """
server_port = None
""" int, optional: The port bound by the default server. """
automatic_label_click = False
""" bool: Whether checkbox/radio actions will try to click the label of invisible elements. """
automatic_reload = True
""" bool: Whether to automatically reload elements as Capybara is waiting. """
enable_aria_label = False
""" bool: Whether fields, links, and buttons will match against aria-label attributes. """
exact = False
""" bool: Whether to match the exact label name/contents. """
default_max_wait_time = 2
""" int: The maximum number of seconds to wait for asynchronous processes to finish. """
default_selector = "css"
""" str: The name of the default selector used to find elements. """
ignore_hidden_elements = True
""" bool: Whether to ignore hidden elements on the page. """
match = "smart"
""" str: The matching strategy to use. """
raise_server_errors = True
""" bool: Whether errors raised in the server should be raised in the tests. """
save_path = None
""" str, optional: Where to put saved pages and screenshots. """
visible_text_only = False
""" bool: Whether to only consider visible text. """
wait_on_first_by_default = False
""" bool: Whether :meth:`find_first` should wait for at least one element to appear. """
servers = {}
# Dict[str, Callable[[object, str, int], None]]: A dictionary of server initialization functions.
drivers = {}
# Dict[str, Callable[[object], object]]: A dictionary of driver initialization functions.
session_name = "default"
""" str: The current session name. """
_session_pool = {}
# Dict[str, Session]: A pool of `Session` objects, keyed by driver and app.
DSL_METHODS = ["using_session", "using_wait_time"]
[docs]def register_server(name):
"""
Register a server initialization function.
Args:
name (str): The name of the server.
Returns:
Callable[[Callable[[object, str, int], None]], None]: A decorator that takes a function
that initializes a server for the given WSGI-compliant app, host, and port.
"""
def register(init_func):
servers[name] = init_func
return register
[docs]def register_driver(name):
"""
Register a driver initialization function.
Args:
name (str): The name of the driver.
Returns:
Callable[[Callable[[object], object], None]: A decorator that takes a function that
initializes a driver for the given WSGI-compliant app.
"""
def register(init_func):
drivers[name] = init_func
return register
[docs]def run_default_server(app, port):
servers["werkzeug"](app, port, server_host)
[docs]def use_default_driver():
""" Use the default driver as the current driver. """
global current_driver
current_driver = None
[docs]@contextmanager
def using_driver(driver):
"""
Execute the wrapped code using a specific driver.
Args:
driver (str): The name of the desired driver.
"""
global current_driver
original_current_driver = current_driver
current_driver = driver
try:
yield
finally:
current_driver = original_current_driver
[docs]@contextmanager
def using_wait_time(seconds):
"""
Execute a context using a specific wait time.
Args:
seconds (int | float): The new wait time.
"""
global default_max_wait_time
original_max_wait_time = default_max_wait_time
default_max_wait_time = seconds
try:
yield
finally:
default_max_wait_time = original_max_wait_time
[docs]def current_session():
"""
Returns the :class:`Session` for the current driver and app, instantiating one if needed.
Returns:
Session: The :class:`Session` for the current driver and app.
"""
driver = current_driver or default_driver
session_key = "{driver}:{session}:{app}".format(
driver=driver, session=session_name, app=str(id(app)))
session = _session_pool.get(session_key, None)
if session is None:
from capybara.session import Session
session = Session(driver, app)
_session_pool[session_key] = session
return session
[docs]@contextmanager
def using_session(name):
"""
Execute the wrapped code using a specific session name.
Args:
name (str): The name of the session to use.
"""
global session_name
previous_session_name = session_name
session_name = name
try:
yield
finally:
session_name = previous_session_name
[docs]def reset_sessions():
""" Resets all sessions. """
for session in iter(_session_pool.values()):
session.reset()
reset = reset_sessions
""" Alias for :func:`reset_sessions`. """
[docs]def string(html):
"""
Wraps the given string, which should contain an HTML document or fragment in a :class:`Simple`
which exposes :class:`MatchersMixin`, :class:`FindersMixin`, and :class:`DocumentMatchersMixin`.
This allows you to query any string containing HTML in the exact same way you would query the
current document in a Capybara session.
Example: A single element ::
node = Capybara.string(\"""<a href="foo">bar</a>\
""")
anchor = node.find_first("a")
anchor["href"] # => "foo"
anchor.text # => "bar"
Example: Multiple elements ::
node = Capybara.string(\"""
<ul>
<li id="home">Home</li>
<li id="projects">Projects</li>
</ul>
\""")
node.find("#projects").text # => "Projects"
node.has_selector("li#home", text="Home")
node.has_selector("#projects")
node.find("ul").find("li:first-child").text # => "Home"
Args:
html (str | lxml.etree.Element): An HTML fragment or document.
Returns:
Simple: A node which has Capybara's finders and matchers.
"""
from capybara.node.simple import Simple
return Simple(html)
@register_server("default")
def init_default_server(app, port, host):
run_default_server(app, port)
@register_server("werkzeug")
def init_werkzeug_server(app, port, host):
try:
import werkzeug
except ImportError:
raise ImportError(
'Capybara\'s werkzeug server is unable to load `werkzeug`, please install the package '
'and add `werkzeug` to your requirements.txt file.')
from werkzeug.serving import make_server
from logging import getLogger
# Mute the server.
log = getLogger('werkzeug')
log.disabled = True
server = make_server(host, port, app, threaded=True)
# Inform Python that it shouldn't wait for request threads to terminate before
# exiting. (They will still be appropriately terminated when the process exits.)
server.daemon_threads = True
server.serve_forever()
@register_driver("selenium")
def init_selenium_driver(app):
try:
import selenium
except ImportError:
raise ImportError(
'Capybara\'s selenium driver is unable to load `selenium`, please install the package '
'and add `selenium` to your requirements.txt file.')
from capybara.selenium.driver import Driver
return Driver(app)
@register_driver("werkzeug")
def init_werkzeug_driver(app):
try:
import werkzeug
except ImportError:
raise ImportError(
'Capybara\'s werkzeug driver is unable to load `werkzeug`, please install the package '
'and add `werkzeug` to your requirements.txt file.')
from capybara.werkzeug.driver import Driver
return Driver(app)