26
PyUIA 0.3 Jeremy Kao / KKBOX SQA / Jun 23, 2015

PyUIA 0.3

Embed Size (px)

Citation preview

Page 1: PyUIA 0.3

PyUIA 0.3Jeremy Kao / KKBOX SQA / Jun 23, 2015

Page 2: PyUIA 0.3

Agenda

● Single Keyword Library● Current Page Tracking● UI Mapping, Page Factory Pattern and

Caching● Handlers Rotation and API Changes● Q&A

Page 3: PyUIA 0.3

Abstraction of Layers

Page 4: PyUIA 0.3

Single Keyword Library

As of v0.3 Prior to v0.3

Page 5: PyUIA 0.3

Less Keyword Conflicts Multiple keywords with name 'Do Something' found.

Give the full name of the keyword you want to use.

Found: 'ThatPage.Do Something' and 'ThisPage.Do Something'

Page 6: PyUIA 0.3

Prior to PyUIA 0.3 - Multiple Libraries

class ThisPage(MyAppLibrary):

_page_class_ios = myapp.ios.ThisPage

_page_class_android = myapp.android.ThatPage

def should_be_on_this_page(self):

self._page_object.wait_for_page_loaded()

def do_something(self):

self._page_object.do_something(target)

class ThatPage(MyAppLibrary):

_page_class_ios = myapp.ios.ThatPage

_page_class_android = myapp.android.ThatPage

def should_be_on_that_page(self):

self._page_object.wait_for_page_loaded()

def do_something(self):

self._page_object.do_something(target)

Page 7: PyUIA 0.3

As of PyUIA 0.3 - Single Library

class MyApp(BaseAppLibrary):

_page_class_ios = ios.ThisPage

_page_class_android = android.ThisPage

def do_something(self):

# for all pages that implement this operation/protocol.

self._page_object.do_something()

return self._current_page.do_something() # return the next page

Page 8: PyUIA 0.3

Bootstrap - Wait For <INIT> Page

class MyApp(BaseAppLibrary):

def wait_for_welcome_page(self):

context = self._current_context

if context.platform == 'android':

from myapp.android import WelcomePage

else:

from myapp.ios import WelcomePage

return get_page_object(WelcomePage, context).wait_for_page_loaded()

Page 9: PyUIA 0.3

Current Page Tracking

● this._current_page always points to current page (of the current device).

● PyUIA updates the current page when a keyword returns a page object.

● The current page determines the runtime behavior of a keyword.

Thanks to Chloe Chen for the idea.

Page 10: PyUIA 0.3

Current Page == Implicit SubjectCurrent page is an analogue to the implied subject in an imperative sentence.

# KeywordsWait For Home PageGo To SettingsEnable SynchronizationGo Back

# Page Objects (Python)

home = HomePage(driver).

wait_for_page_loaded()

settings = home.go_to_settings()

settings.enable_synchronization()

settings.go_back() # Home

Page 11: PyUIA 0.3

PageFactory Impl. in Python?Refer to PageFactory (Java)...● PageFactory implementation from selenium for python - Stack Overflow

(Nov 8, 2013)● Does Webdriver support pagefactory for Python? - Stack Overflow (Dec

12, 2012)● PageFactory Implementation in Python - Google Groups (May 8, 2012)● PageFactory implementation in Python? - Google Groups (Aug 11, 2011)

Unfortunately, we have to invent the wheel ~ Implementing PageFactory Pattern in Python (June 10, 2015) (find_by, @cacheable)

Page 12: PyUIA 0.3

Google Search Pagefrom pyuia.selenium import SeleniumPageObject as PageObject, find_by, cacheable

from selenium.webdriver.common.by.By

class GoogleSearchPage(PageObject):

_search_box = find_by(how=By.NAME, using='q')

_search_button = find_by(name='btnK')

def search(self, keywords):

self._search_box().click()

self._search_box().send_keys(keywords) # look up twice?

self._search_button().click()

The following keyword arguments are supported for various locator strategies: id_ (to avoid conflict with the built-in keyword id), name, class_name, css_selector, tag_name, xpath, link_text, partial_link_text.

Page 13: PyUIA 0.3

find_bydef find_by(how=None, using=None, multiple=False, cacheable=True, if_exists=False,

context=None, driver_attr='_driver', **kwargs):

● multiple – the answer to @FindBy for a list of elements.● cacheable – the answer to @CacheLookup. (see also

@cacheable for customized lookup.)● if_exists - for conditional UI elements. ● context – the answer to @FindBys.● driver_attr – the attribute name for getting the

reference to WebDriver. Defaults to ’_driver’.

Page 14: PyUIA 0.3

find_by (cont.)# prior to v0.3

def _clickme_button(self):

return self._driver.find_element_by_name('Click Me!')

def _clickme_button(self):

return self._driver.find_element_by_id('click_me')

# as of v0.3

_clickme_button = find_by(name='Click Me!')

_clickme_button = find_by(id_='click_me')

Page 15: PyUIA 0.3

find_by (cont.)

# prior to v0.3

def _products(self):

container = self._driver.find_element_by_id('products_list')

return container.find_elements_by_class_name('android.widget.TextView')

# as of v0.3

_products = find_by(class_name='android.widget.TextView', multiple=True,

context=find_by(id_='products_list'))

Page 16: PyUIA 0.3

find_by (cont.)

# prior to v0.3

def _tip_if_exists(self):

try:

return self._driver_find_element_by_id('tip')

except NoSuchElementException:

return None

# as of v0.3

_tip_if_exists = find_by(id_='tip', if_exists=True)

Page 17: PyUIA 0.3

@cacheable (for customized lookup)

@cacheable

def _categories(self):

candidates = self._driver.find_elements_by_id('list')

assert len(candidates) == 2, candidates

container = candidates[1] # the 2nd one

return container.find_elements_by_class_name('android.widget.TextView')

Page 18: PyUIA 0.3

Handlers Rotation def assert_on_this_page(self):

self._assert_any_present(

[self._lyrics_button, self._discovery_tutorial_view_if_exists],

handlers={

self._other_device_playing_alert: self._dismiss_devices_alert, # H1

self._storage_space_alert: self._dismiss_storage_alert, # H2

self._high_quality_prompt_message: self._dismiss_high_quality_prompt # H3

})

# ...

● Prior to v0.3 - (H1 ➔ H2 ➔ H3) ➥ (H1 ➔ H2 ➔ H3) ...● As of v0.3 - H1 ➥ H2 ➥ H3 ➥ H1 ➥ H3 ➥ H1 ➥ H1 ...

Page 19: PyUIA 0.3

Modeling: Page and UI Fragment

MainPage

Menu

ActionBarclass Menu(class):

def open_menu(self): pass

class ActionBar(class):

def title(self): pass

class MainPage(AppiumPO, Menu, ActionBar): pass

Page 20: PyUIA 0.3

Page-to-page Transitions

A(WelcomePage)

_go_to(page_class)

_back_to(page_class)

B(MainPage)

● assert_on_this_page(from_page_class) - assertion, no waiting● _get_page_entry_handlers(from_page_class) - rotation● _on_page_entry(from_page_class) - replace _wait_for_idle.

Page 21: PyUIA 0.3

Page-to-page Transitions (cont.)

# Page A

_clickme_button = find_by(id_='click_me')

def go_to_b(self):

self._clickme_button().click()

return self._go_to(BPage)

# Page B

_back_button = find_by(name='Back')

def go_back(self):

self._back_button().click()

# go to the previous page by default

return self._back_to()

_go_to(page_class)

_back_to(page_class=None)

Page 22: PyUIA 0.3

Page-to-page Transitions (cont.)# WelcomePage

def assert_on_this_page(self, from_page_class):

self._assert_present(self._start_button)

def start(self):

self._start_button().click() # look up again?

self._go_to(MainPage)

Page 23: PyUIA 0.3

Page-to-page Transitions (cont.)

# MainPage

def assert_on_this_page(self, from_page_class):

self._assert_present(self._main_menu)

def _get_page_entry_handlers(self, from_page_class):

if from_page_class == WelcomePage:

return { self._network_error: self._retry }

Page 24: PyUIA 0.3

Page-to-page Transitions (cont.)# MainPage

def _on_page_entry(self, from_page_class):

if from_page_class != WelcomePage: return

self._wait_disappear(self._loading_indicator)

self._watch({

self._chromecast_ok_button: self._dismiss_chromecast_tip,

self._tutorial_ok_button: self._dismiss_tutorial,

}, 10) # max duration

return True # indicates UI changed (takes another screenshot)

Where _watch (handlers, max_duration) replaces _handle_conditional_views (handlers, duration)

Page 26: PyUIA 0.3

Q&AThanks you all for listening ~