selenide alternative in practice - implementation & lessons learned [seleniumcamp 2016]

118
Selenide Alternative in Practice Implementation & Lessons Learned

Upload: yasha-kramarenko

Post on 07-Jan-2017

921 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide Alternative in PracticeImplementation & Lessons Learned

Page 2: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Preface…

Page 3: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Implementation & Lessons Learned

Selenide Alternative in Practice

Page 4: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide

Page 5: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = ?

Page 6: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser(it should be already

automated;)

Page 7: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

Page 8: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

Page 9: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

Page 10: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

Page 11: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide = Effectiveweb test automation tool

=

tool to automate web UI tests logic

not browser

concise API

waiting search

waiting asserts

dynamic elements

Page 12: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide Alternative?

concise API

waiting search

waiting asserts

dynamic elements

Page 13: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide Alternative?

concise API

waiting search

waiting asserts

dynamic elements

How should it work?

Page 14: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Selenide Alternative?

concise API

waiting search

waiting asserts

dynamic elements

How should it work? How to build?

Page 15: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

With examples in

Page 16: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

ProvisoPartial sacrificing

DRY, privatisation, etc. for simplification of examples

Page 17: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]
Page 18: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Concise API

Page 19: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

Page 20: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

DRY locator helpers

Page 21: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

Page 22: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

OOP

Page 23: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

from selenium import webdriver driver = webdriver.Firefox() # ... OOP

Page 24: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

Modular

from selenium import webdriver driver = webdriver.Firefox() # ... OOP

Page 25: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]"))

# vs

s(with_text('task'))

Modular

from selenium import webdriver driver = webdriver.Firefox() # ...

from selene.tools import set_driver, s set_driver(webdriver.Firefox()) # ...

OOP

Page 26: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]")).

# vs

s(with_text('task'))

Modular

OOPfrom selenium import webdriver driver = webdriver.Firefox() driver.get(‘http://todomvc4tasj.herokuapp.com’)

from selene.tools import set_driver, s set_driver(webdriver.Firefox()) visit(‘http://todomvc4tasj.herokuapp.com’)

Page 27: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element(By.XPATH, "//*[contains(text(),'task')]")

# vs

s(by_xpath("//*[contains(text(),'task')]")).

# vs

s(with_text('task'))

Functions over Methods for main actions

from selenium import webdriver driver = webdriver.Firefox() driver.get(‘http://todomvc4tasj.herokuapp.com’)

from selene.tools import set_driver, s set_driver(webdriver.Firefox()) visit(‘http://todomvc4tasj.herokuapp.com’)

Page 28: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

s(by_css(‘#new-todo’))

# vs/or

s('#new-todo')

Convenient defaults

Page 29: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element_by_css_selector('#new-todo').clear()

driver.find_element_by_css_selector(‘#new-todo’)\ .send_keys(‘task')

# vs

s(‘#new-todo’).clear().send_keys(‘task’)

Page 30: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element_by_css_selector('#new-todo').clear()

driver.find_element_by_css_selector(‘#new-todo’)\ .send_keys(‘task')

# vs

s(‘#new-todo’).clear().send_keys(‘task’)

Chainable methods

Page 31: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element_by_css_selector('#new-todo')\

.send_keys('task', Keys.ENTER)

# vs

s('#new-todo').send_keys('task').press_enter()

Page 32: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

driver.find_element_by_css_selector('#new-todo')\

.send_keys('task', Keys.ENTER)

# vs

s('#new-todo').send_keys('task').press_enter()

“Short-cut” methods

Page 33: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

s(‘#new-todo’).clear().send_keys(‘task’)

# vs

s(‘#new-todo’).set(‘task’)

“Short-cut” methods

Page 34: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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)

Page 35: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

new_todo = by_css('#new-todo')visit('http://todomvc4tasj.herokuapp.com')s(new_todo).set('task 1').press_enter() # ...s(new_todo).set('task 2').press_enter() # vs new_todo = s('#new-todo')visit('http://todomvc4tasj.herokuapp.com')new_todo.set('task 1').press_enter() # ...new_todo.set('task 2').press_enter()

Page 36: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

new_todo = by_css('#new-todo')visit('http://todomvc4tasj.herokuapp.com') s(new_todo).set('task 1').press_enter() # ...s(new_todo).set('task 2').press_enter() # vs new_todo = s('#new-todo')visit('http://todomvc4tasj.herokuapp.com') new_todo.set('task 1').press_enter() # ...new_todo.set('task 2').press_enter()

Page 37: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

new_todo = by_css('#new-todo')visit('http://todomvc4tasj.herokuapp.com') s(new_todo).set('task 1').press_enter() # ...s(new_todo).set('task 2').press_enter() # vs new_todo = s('#new-todo')visit('http://todomvc4tasj.herokuapp.com') new_todo.set('task 1').press_enter() # ...new_todo.set('task 2').press_enter()

?

Page 38: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Dynamic Elements

Page 39: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Dynamic Elementsaka Lazy Proxy Elements

Page 40: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

new_todo = s('#new-todo')

Page 41: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def s(css_selector_or_locator): return SElement(css_selector_or_locator)

Element Factory

Page 42: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 43: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 44: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 45: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 46: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 47: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

“waiting search”

Page 48: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

“waiting assert”

Page 49: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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), ...)

Page 50: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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), ...)

Page 51: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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), ...)

Page 52: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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), ...)

Page 53: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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()

Page 54: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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()

Page 55: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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

Page 56: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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 simpleand secure

but less powerful

Page 57: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

Page 58: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self insist = assure should = assure should_be = assure should_have = assure def click(self): return self.do(lambda element: element.click())

Page 59: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Speed?

Page 60: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, css_selector_or_locator, context=...): self.locator = parse(css_selector_or_locator) # ... def finder(self): return self.context.find_element(*self.locator)

def assure(self, condition): wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command(self.finder()) return self def click(self): return self.do(lambda element: element.click())

redundant finder call

Page 61: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def finder(self): return self.context.find_element(*self.locator)

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

reusing self.found

Page 62: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def finder(self): return self.context.find_element(*self.locator)

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

even when not neededwait always

Page 63: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): try: self.refind() command() except (WebDriverException): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

smarter:)

Page 64: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): try: self.refind() command() except (WebDriverException): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

smarter:)

Makes Selene as fast as Selenium “with research” :)

Page 65: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): try: self.refind() command() except (WebDriverException): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

Page 66: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def refind(self): self.found = self.finder() return self.found

def assure(self, condition): self.found = wait_for(self, condition, config.timeout) return self

def do(self, command): try: self.refind() command() except (WebDriverException): self.assure(visible) command() return self def click(self): return self.do(lambda: self.found.click())

What if I sometimes…I want “raw selenium” speed?

Page 67: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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

Page 68: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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 :)

Page 69: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Proof Demo

Page 70: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Total Laziness

Page 71: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

todo_list = s('#todo-list') active_tasks = todo_list.find_all(‘.active’) first_completed_task = todo_list.find(‘.completed')

visit('http://todomvc4tasj.herokuapp.com')# ...

Usage

Page 72: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

tasks = ss(‘#todo-list>li') first_task = tasks.get(1) task_a = tasks.find(exact_text(“a")) visible_tasks = tasks.filter(visible)

visit('http://todomvc4tasj.herokuapp.com')# ...

Usage

Page 73: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

visible_tasks = ss(‘#todo-list>li').filter(visible) visit('http://todomvc4tasj.herokuapp.com') # ...

Page 74: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def ss(css_selector_or_locator): return SElementsCollection(css_selector_or_locator)

Page 75: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElementsCollection(...): def __init__(self, css_selector_or_locator, ..., ...,): self.locator = parse(css_selector_or_locator) ... def finder(self): return ... def filter(self, condition): return ...

...

Page 76: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElementsCollection(...): def __init__(self, css_selector_or_locator, ..., ...,): self.locator = parse(css_selector_or_locator) ... def finder(self): return ... def filter(self, condition): return ...

...

Page 77: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

locator = lambda index: '%s[%s]' % (self.locator, index) webelements = self.context.find_elements(*self.locator)

return [SElement(locator(index)).cash_with(element) for index, element in enumerate(webelements)]

SElementsCollection#finder

Page 78: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

locator = lambda index: '%s[%s]' % (self.locator, index) webelements = self.context.find_elements(*self.locator)

return [SElement(locator(index)).cash_with(element) for index, element in enumerate(webelements)]

SElementsCollection#finder

Page 79: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

locator = lambda index: '%s[%s]' % (self.locator, index) webelements = self.context.find_elements(*self.locator)

return [SElement(locator(index)).cash_with(element) for index, element in enumerate(webelements)]

SElementsCollection#finder

Page 80: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElementsCollection(...): def __init__(self, css_selector_or_locator, ..., ...,): self.locator = parse(css_selector_or_locator) ... def finder(self): return ... def filter(self, condition): return FilteredSElementsCollection(self, condition) ...

Page 81: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class FilteredSElementsCollection(SElementsCollection): def __init__(self, selements_collection, condition): self.coll = selements_collection self.condition = condition # ... def finder(self): filtered_elements = ... return filtered_elements

Page 82: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class FilteredSElementsCollection(SElementsCollection): def __init__(self, selements_collection, condition): self.coll = selements_collection self.condition = condition # ... def finder(self): filtered_elements = ... return filtered_elements

Page 83: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class FilteredSElementsCollection(SElementsCollection): def __init__(self, selements_collection, condition): self.coll = selements_collection self.condition = condition # ... def finder(self): filtered_elements = ... return filtered_elements

Page 84: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

self.coll = selements_collection self.condition = condition locator = "(%s).filter(%s)" % ( self.coll.locator, self.condition.__class__.__name__) SElementsCollection.__init__(self, ('selene', locator)) # ...

FilteredSElementsCollection#__init__

Page 85: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

self.coll = selements_collection self.condition = condition locator = "(%s).filter(%s)" % ( self.coll.locator, self.condition.__class__.__name__) SElementsCollection.__init__(self, ('selene', locator)) # ...

FilteredSElementsCollection#__init__

Page 86: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

filtered_elements = [selement for selement in self.coll if self.condition(selement)] return filtered_elements

FilteredSElementsCollection#finder

Page 87: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Meta-programming?

Page 88: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self, ..., context=...): # …

def finder(self): return self.context.find_element(*self.locator)

# ...class SElementsCollection(...): def __init__(self, ..., context=..., selement_class=…): # ... # ...

Page 89: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def __init__(self, ..., context=RootSElement()):

Page 90: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class RootSElement(object): def __getattr__(self, item): return getattr(selene.tools.get_driver(), item)

The most innocent example :)

Page 91: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def __getattr__(self, item): return self.do(lambda: getattr(self.found, item))

# VS

def click(self): return self.do(lambda: self.found.click()) def submit(self): return self.do(lambda: self.found.submit()) def clear(self): return self.do(lambda: self.found.clear())

...

Proxying vs Overriding

Page 92: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def __getattr__(self, item): return self.do(lambda: getattr(self.found, item))

# VS

def click(self): return self.do(lambda: self.found.click()) def submit(self): return self.do(lambda: self.found.submit()) def clear(self): return self.do(lambda: self.found.clear())

...

Proxying vs Overriding

Page 93: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Widgets

Page 94: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click() def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete()

Just works :)

Page 95: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click() def test_custom_selement(): given_active("a", "b") Task("#todo-list>li:nth-child(1)").delete()

Just works :)

Page 96: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Because…class SElement(...): def __init__(self, ..., context=RootSElement()): #...

def finder(self): return self.context.find_element(*self.locator) def s(self, css_selector_or_locator): return SElement(css_selector_or_locator, context=self)

#...

Page 97: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Because…class SElement(...): def __init__(self, ..., context=RootSElement()): #...

def finder(self): return self.context.find_element(*self.locator) def s(self, css_selector_or_locator): return SElement(css_selector_or_locator, context=self)

#...

Page 98: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Collection of widgets

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click() def test_selements_collection_of_custom_selements(): given_active("a", "b", "c") for task in ss("#todo-list>li", of=Task): task.delete() ss("#todo-list>li", of=Task).should_be(empty)

Page 99: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Collection of widgets

class Task(SElement): def delete(self): self.hover() self.s(".destroy").click() def test_selements_collection_of_custom_selements(): given_active("a", "b", "c") for task in ss("#todo-list>li", of=Task): task.delete() ss("#todo-list>li", of=Task).should_be(empty)

Page 100: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElementsCollection(...): def __init__(self, ..., ...): ... ... ... def finder(self): ... return [ SElement(locator(index)).cash_with(element) for index, element in enumerate(webelements)]

...

recalling basic implementation…

Page 101: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElementsCollection(...): def __init__(self, ..., ..., of=SElement): ... self.wrapper_class = of ... def finder(self): ... return [ self.wrapper_class(locator(index)).cash_with(element) for index, element in enumerate(webelements)]

...

needs more…

Page 102: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

and sometimes even much more… including meta-programming…

Page 103: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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() ...

Page 104: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

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")) =>

Page 105: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

self.coll = selements_collection self.condition = condition locator = "(%s).found_by(%s)" % ( self.coll.locator, self.condition.__class__.__name__) SElementsCollectionElementByCondition.__init__(self, (“selene”, locator)) ...

SElementsCollectionElementByCondition#__init__

Page 106: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

self.coll = selements_collection self.condition = condition locator = "(%s).found_by(%s)" % ( self.coll.locator, self.condition.__class__.__name__) SElementsCollectionElementByCondition.__init__(self, (“selene”, locator)) extend(self, self.coll.wrapper_class, ("selene", locator))

SElementsCollectionElementByCondition#__init__

Page 107: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def extend(obj, cls, *init_args, **init_kwargs): obj.__class__ = type(obj.__class__.__name__, (obj.__class__, cls), {}) cls.__init__(obj, *init_args, **init_kwargs)

Dynamic class extension

Page 108: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def extend(obj, cls, *init_args, **init_kwargs): obj.__class__ = type(obj.__class__.__name__, (obj.__class__, cls), {}) cls.__init__(obj, *init_args, **init_kwargs)

Dynamic class extension

Page 109: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

def extend(obj, cls, *init_args, **init_kwargs): obj.__class__ = type(obj.__class__.__name__, (obj.__class__, cls), {}) cls.__init__(obj, *init_args, **init_kwargs)

Dynamic class extension

e.g. to initialise probable widget’s subelements

Page 110: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class Task(SElement): def init(self): self.destroy_button = self.s(".destroy") ... ... task = ss("#todo-list>li", of=Task).find(text("a")): task.hover() task.destroy_button.click() ...

like in this example ;)

Page 111: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

__init__ vs init ? o_O

Page 112: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

class SElement(...): def __init__(self): ... if hasattr(self, 'init'): self.init()

__init__ vs init ? o_O

Page 113: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Proxying vs dynamic extensionSElement(...) def __getattr__(self, item): return self.do(lambda: getattr(self.found, item))

SElementsCollectionElementByCondition(SElement) __init__ extend(self, self.coll.wrapper_class, ('selene', locator))

# VS

SElement(...) def __getattr__(self, item): return self.do(lambda: getattr(self.found, item))

SElementsCollectionElementByCondition(SElement) __init__ extend(self, self.coll.wrapper_class, ('selene', locator))

Page 114: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

Install & Docs

Page 115: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

$ pip install selene

Page 116: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

https://github.com/yashaka/selene

Page 117: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

visit(‘/questions_and_answers') s('#question').set('<YOUR QUESTION>’).press_enter() ss('.answer').should_not_be(empty)

Page 118: Selenide Alternative in Practice - Implementation & Lessons learned [SeleniumCamp 2016]

github.com/yashaka

github.com/yashaka/selene

[email protected]

@yashaka

Thank You