Source code for capybara.werkzeug.node
from itertools import chain
from xpath import dsl as x
from xpath.renderer import to_xpath
from capybara.driver.node import Node as Base
from capybara.exceptions import ReadOnlyElementError, UnselectNotAllowed
from capybara.helpers import normalize_whitespace
from capybara.node.simple import Simple
from capybara.utils import inner_text
[docs]class Node(Base):
@property
def tag_name(self):
return self.native.tag
def __getitem__(self, name):
return self._string_node[name]
def __eq__(self, other):
return self.native == other.native
def __hash__(self):
return hash(self.native)
@property
def all_text(self):
return normalize_whitespace(inner_text(self.native))
@property
def visible_text(self):
return normalize_whitespace(self.unnormalized_text())
[docs] def unnormalized_text(self, check_ancestor_visibility=True):
if not self._string_node._visible(check_ancestor_visibility):
return ""
else:
parts = (
[self.native.text] +
list(chain(*(
[Node(self.driver, c).unnormalized_text(check_ancestor_visibility=False),
c.tail]
for c in self.native.getchildren()))))
return "".join(filter(None, parts))
@property
def disabled(self):
if self._string_node.disabled:
return True
if self.tag_name in ["option", "optgroup"]:
return self._find_xpath("parent::*[self::optgroup or self::select]")[0].disabled
else:
return any(self._find_xpath(
"parent::fieldset[@disabled] | "
"ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]"))
@property
def readonly(self):
return self._string_node.readonly
@property
def multiple(self):
return self._string_node.multiple
@property
def path(self):
return self._string_node.path
@property
def checked(self):
return self._string_node.checked
@property
def selected(self):
return self._string_node.selected
@property
def visible(self):
return self._string_node.visible
@property
def value(self):
return self._string_node.value
[docs] def click(self, *keys, **offset):
if any(keys) or any(offset.values()):
raise ValueError("The Werkzeug driver does not support click options")
if self.tag_name == "a" and self["href"]:
self.driver.follow("GET", self["href"])
elif (self.tag_name == "input" and self["type"] in ["submit", "image"]) or \
(self.tag_name == "button"):
associated_form = self._form
if associated_form is not None:
from capybara.werkzeug.form import Form
Form(self.driver, associated_form).submit(self)
elif self.tag_name == "input" and self["type"] in ["checkbox", "radio"]:
self.set(not self.checked)
elif self.tag_name == "label":
labeled_controls = (
self._find_xpath("//input[@id='{}']".format(self["for"])) if self["for"]
else self._find_xpath(".//input"))
labeled_control = labeled_controls[0] if len(labeled_controls) else None
if labeled_control and (labeled_control._is_checkbox or labeled_control._is_radio):
labeled_control.set(not labeled_control.checked)
[docs] def set(self, value):
if self.readonly:
raise ReadOnlyElementError
if self._is_radio:
self._set_radio(value)
elif self._is_checkbox:
self._set_checkbox(value)
elif self._is_input_field:
self._set_input(value)
elif self._is_textarea:
self._set_textarea(value)
[docs] def select_option(self):
if self.disabled:
return
select = self.native.xpath("ancestor::select")[0]
if select.get("multiple", None) != "multiple":
options = select.xpath(".//option[@selected='selected']")
for option in options:
option.attrib.pop("selected", None)
self.native.set("selected", "selected")
[docs] def unselect_option(self):
select = self.native.xpath("ancestor::select")[0]
if select.get("multiple", None) != "multiple":
raise UnselectNotAllowed()
self.native.attrib.pop("selected", None)
@property
def _is_input_field(self):
return self.tag_name == "input"
@property
def _is_checkbox(self):
return self._is_input_field and self["type"] == "checkbox"
@property
def _is_radio(self):
return self._is_input_field and self["type"] == "radio"
@property
def _is_textarea(self):
return self.tag_name == "textarea"
@property
def _string_node(self):
return Simple(self.native)
@property
def _form(self):
elements = (
self.native.xpath("//form[@id='{}']".format(self["form"])) if self["form"]
else self.native.xpath(".//ancestor::form"))
return elements[0] if elements else None
def _find_css(self, css):
return self._find_xpath(to_xpath(x.css(css)))
def _find_xpath(self, xpath):
cls = type(self)
elements = self.native.xpath(xpath)
return [cls(self.driver, element) for element in elements]
def _set_radio(self, value):
other_radios = self.native.xpath(to_xpath(
x.anywhere("input")[x.attr("name") == self["name"]]))
for node in other_radios:
node.attrib.pop("checked", None)
self.native.set("checked", "checked")
def _set_checkbox(self, value):
if value:
self.native.set("checked", "checked")
else:
self.native.attrib.pop("checked", None)
def _set_input(self, value):
if self["maxlength"]:
maxlength = int(self["maxlength"])
value = value[0:maxlength]
self.native.set("value", value)
def _set_textarea(self, value):
for child in self.native.getchildren():
self.native.remove(child)
self.native.text = value