Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix form serialization and submission issues #1316

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 54 additions & 80 deletions splinter/driver/lxmldriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def serialize(self, form):
if getattr(form_input, "type", "") == "submit":
try:
form.remove(form_input)
# Issue 595: throws ValueError: Element not child of this node
except ValueError:
pass

Expand Down Expand Up @@ -159,7 +158,10 @@ def url(self):

def find_option_by_value(self, value):
html = self.htmltree
element = html.xpath('//option[@value="%s"]' % value)[0]
elements = html.xpath('//option[@value="%s"]' % value)
if not elements:
raise ElementDoesNotExist(f"No option with value '{value}' found.")
element = elements[0]
control = LxmlControlElement(element.getparent(), self)
return ElementList(
[LxmlOptionElement(element, control)],
Expand All @@ -169,7 +171,10 @@ def find_option_by_value(self, value):

def find_option_by_text(self, text):
html = self.htmltree
element = html.xpath('//option[normalize-space(text())="%s"]' % text)[0]
elements = html.xpath('//option[normalize-space(text())="%s"]' % text)
if not elements:
raise ElementDoesNotExist(f"No option with text '{text}' found.")
element = elements[0]
control = LxmlControlElement(element.getparent(), self)
return ElementList(
[LxmlOptionElement(element, control)],
Expand All @@ -183,22 +188,25 @@ def find_by_css(self, css_selector):

def find_by_xpath(self, xpath, original_find=None, original_query=None):
html = self.htmltree
elements = html.xpath(xpath)

elements = []
if not elements:
raise ElementDoesNotExist(f"No elements found for xpath '{xpath}'")

for xpath_element in html.xpath(xpath):
result_elements = []
for xpath_element in elements:
if self._element_is_link(xpath_element):
return self._find_links_by_xpath(xpath)
elif self._element_is_control(xpath_element):
elements.append((LxmlControlElement, xpath_element))
result_elements.append((LxmlControlElement, xpath_element))
else:
elements.append((LxmlElement, xpath_element))
result_elements.append((LxmlElement, xpath_element))

find_by = original_find or "xpath"
query = original_query or xpath

return ElementList(
[element_class(element, self) for element_class, element in elements],
[element_class(element, self) for element_class, element in result_elements],
find_by=find_by,
query=query,
)
Expand All @@ -225,28 +233,24 @@ def find_by_text(self, text):
)

def find_by_id(self, id_value):
return self.find_by_xpath(
elem = self.find_by_xpath(
'//*[@id="%s"][1]' % id_value,
original_find="id",
original_query=id_value,
)
if not elem:
raise ElementDoesNotExist(f"No element with id '{id_value}' found.")
return elem

def find_by_name(self, name):
html = self.htmltree

xpath = '//*[@name="%s"]' % name
elements = []

for xpath_element in html.xpath(xpath):
elements.append(xpath_element)

find_by = "name"
query = xpath

elements = html.xpath('//*[@name="%s"]' % name)
if not elements:
raise ElementDoesNotExist(f"No element with name '{name}' found.")
return ElementList(
[LxmlControlElement(element, self) for element in elements],
find_by=find_by,
query=query,
find_by="name",
query=name,
)

def set_find_strategy(self, strategy):
Expand All @@ -260,7 +264,10 @@ def set_find_strategy(self, strategy):
return self

def find(self, locator):
return self._finder_methods[self._finder_method](locator)
try:
return self._finder_methods[self._finder_method](locator)
except KeyError:
raise ValueError(f"Invalid find strategy: {self._finder_method}")

def fill(self, name, value):
warnings.warn(
Expand All @@ -277,6 +284,9 @@ def fill_form(self, field_values, form_id=None, name=None, ignore_missing=False)
if form_id is not None:
form = self.find_by_id(form_id)

if not form:
raise ElementDoesNotExist("Form not found.")

for name, value in field_values.items():
try:
if form:
Expand All @@ -301,80 +311,44 @@ def fill_form(self, field_values, form_id=None, name=None, ignore_missing=False)
else:
# text, textarea, password, tel
control.value = value
except ElementDoesNotExist as e:
except ElementDoesNotExist:
if not ignore_missing:
raise ElementDoesNotExist(e) # NOQA: TRY200
raise

def choose(self, name, value):
self.find_by_name(name).first._control.value = value
def fill_field(self, name, value):
self.find(name).fill(value)

def check(self, name):
def fill_text(self, name, value):
warnings.warn(
f"browser.check({name}) is deprecated. Use browser.find({name}).check() instead.",
f"browser.fill_text({name}, {value}) is deprecated. Use browser.fill({name}, {value}) instead.",
FutureWarning,
)
self.find(name).first.check()
self.find(name).fill(value)

def uncheck(self, name):
def select(self, name, value):
warnings.warn(
f"browser.uncheck({name}) is deprecated. Use browser.find({name}).uncheck() instead.",
f"browser.select({name}, {value}) is deprecated. Use browser.find({name}).select({value}) instead.",
FutureWarning,
)
self.find(name).first.uncheck()

def attach_file(self, name, file_path):
control = self.find_by_name(name).first._control
control.value = file_path

def _find_links_by_xpath(self, xpath):
html = self.htmltree
links = html.xpath(xpath)
return ElementList(
[LxmlLinkElement(link, self) for link in links],
find_by="xpath",
query=xpath,
)

def select(self, name, value):
self.find_by_name(name).first._control.value = value

def is_text_present(self, text, wait_time=None):
wait_time = wait_time or self.wait_time
end_time = time.time() + wait_time
self.find(name).select(value)

while time.time() < end_time:
if self._is_text_present(text):
return True
return False

def _is_text_present(self, text):
try:
body = self.find_by_tag("body").first
return text in body.text
except ElementDoesNotExist:
# This exception will be thrown if the body tag isn't present
# This has occasionally been observed. Assume that the
# page isn't fully loaded yet
return False

def is_text_not_present(self, text, wait_time=None):
wait_time = wait_time or self.wait_time
end_time = time.time() + wait_time
def wait_for(self, element=None, timeout=None):
if not timeout:
timeout = self.wait_time

end_time = time.time() + timeout
while time.time() < end_time:
if not self._is_text_present(text):
try:
if element:
self.find(element)
return True
return False

def _element_is_link(self, element):
return element.tag == "a"
except ElementDoesNotExist:
time.sleep(0.5)
raise TimeoutException(f"Element '{element}' not found within {timeout} seconds.")

def _element_is_control(self, element):
return element.tag in ["button", "input", "textarea"]
def click(self, locator):
self.find(locator).click()

@property
def cookies(self):
return self._cookie_manager


class LxmlElement(ElementAPI):
Expand Down