This document describes an alternative to the Selenide web test automation tool called Selenide Alternative. It discusses lessons learned from implementing the alternative tool, including using a more concise API, implicit waits, assertion waits, and handling dynamic elements. The alternative aims to improve on Selenide by using object-oriented programming, modular design, and lazy proxy elements to dynamically find and wait for elements.
6. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
(it should be already
automated;)
7. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
concise API
…
…
…
8. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
concise API
waiting search
…
…
9. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
concise API
waiting search
waiting asserts
…
10. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
concise API
waiting search
waiting asserts
dynamic elements
11. Selenide = Effective
web test automation tool
=
tool to automate
web UI tests logic
not browser
concise API
waiting search
waiting asserts
dynamic elements
34. Built in Explicit Waits aka Waiting
Asserts
WebDriverWait(driver, 4).until(
element_to_be_clickable(by_css('#new-todo')))
# vs
s('#new-todo').should_be(clickable)
49.
def wait_for(entity, condition, timeout):
# ...
end_time = time.time() + timeout
while True:
try:
value = condition(entity)
if value is not None:
return value
except (WebDriverException,) as exc:
# ...
time.sleep(config.poll_during_waits)
if time.time() > end_time:
break
raise TimeoutException(
"""
failed while waiting %s seconds
to assert %s%s
""" % (timeout, condition.__class__.__name__,
str(condition), ...)
50.
def wait_for(entity, condition, timeout):
# ...
end_time = time.time() + timeout
while True:
try:
value = condition(entity)
if value is not None:
return value
except (WebDriverException,) as exc:
# ...
time.sleep(config.poll_during_waits)
if time.time() > end_time:
break
raise TimeoutException(
"""
failed while waiting %s seconds
to assert %s%s
""" % (timeout, condition.__class__.__name__,
str(condition), ...)
51.
def wait_for(entity, condition, timeout):
# ...
end_time = time.time() + timeout
while True:
try:
value = condition(entity)
if value is not None:
return value
except (WebDriverException,) as exc:
# ...
time.sleep(config.poll_during_waits)
if time.time() > end_time:
break
raise TimeoutException(
"""
failed while waiting %s seconds
to assert %s%s
""" % (timeout, method.__class__.__name__,
str(condition), ...)
52.
def wait_for(entity, condition, timeout):
# ...
end_time = time.time() + timeout
while True:
try:
value = condition(entity)
if value is not None:
return value
except (WebDriverException,) as exc:
# ...
time.sleep(config.poll_during_waits)
if time.time() > end_time:
break
raise TimeoutException(
"""
failed while waiting %s seconds
to assert %s%s
""" % (timeout, method.__class__.__name__,
str(condition), ...)
53. class Visible(object):
def __call__(self, selement):
self.selement = selement
found = selement.finder()
return found if found.is_displayed() else None
def __str__(self):
return """
for element found by: %s...
""" % (self.selement.locator, …)
visible = Visible()
54. class Visible(object):
def __call__(self, selement):
self.selement = selement
found = selement.finder()
return found if found.is_displayed() else None
def __str__(self):
return """
for element found by: %s...
""" % (self.selement.locator, …)
visible = Visible()
55. class Visible(object):
def __call__(self, webelement):
self.selement = selement
found = selement.finder()
return webelement.is_displayed()
def __str__(self):
return """
for element found by: %s...
""" % (self.selement.locator, …)
visible = Visible()
Original jSelenide style
with selement.finder() being moved to wait_for
56. class Visible(object):
def __call__(self, webelement):
self.selement = selement
found = selement.finder()
return webelement.is_displayed()
def __str__(self):
return """
for element found by: %s...
""" % (self.selement.locator, …)
visible = Visible()
Original jSelenide style
with selement.finder() being moved to wait_for
more simple
and secure
but
less powerful
68.
def cash(self):
self.is_cached = True
self.finder = lambda: self.found
return self
f
def do(self, command):
try:
if not self.is_cached:
self.refind()
command()
# ...
self.assure(visible)
command()
return self
def click(self):
return self.do(lambda: self.found.click())
Introducing simple cashing
Making Selene almost as fast
as raw Selenium :)
103. Collection of widgets:
selection of one of them
visible_tasks = ss("#todo-list>li", of=Task).filter(visible)
...
...
task = visible_tasks.find(exact_text("a")):
task.delete()
...
104. class SElementsCollectionElementByCondition(SElement):
def __init__(self, selements_collection, condition):
self.coll = selements_collection
self.condition = condition
locator = "(%s).found_by(%s)" % (
self.coll.locator,
self.condition.__class__.__name__)
...
def finder(self):
for selement in self.coll:
if self.condition(selement):
return selement.found
find(exact_text("a")) =>