Package pom_elements
The Python package pom-elements
for a Page Object Model that extends to Page Elements.
pom-elements
allows for creating Page Object Models for your testing needs and
extending the model to Elements on the Page. The project is currently only supporting
Selenium webdriver instance, but is intended to help test writers abstract testing
organization around web page development choices.
Examples - Elements
Write tests using any Selenium Locator
(i.e. css, id, xpath, name, etc.) of your choosing.
>>> element = MultiElement(
... css="#recordlist li:nth-child(4)",
... webdriver=selenium,
... timeout=5)
...
>>> element.is_visible()
True
>>> element.click()
Or simplify complex xpath arguments with XPathElement.
>>> xpath_element = XPathElement(
... webdriver=selenium,
... xpath='//a',
... foo='bar',
... baz='spam',
... timeout=3
... )
>>> xpath_element.xpath
'//a[@foo="bar"][@baz="spam"]'
>>> xpath_element.is_invisible()
False
>>> xpath_element.can_be_clicked()
True
Examples - Page and PageObject
Organize your tests around a Page.
>>> class GoogleHomePage(Page):
... search_bar = XPathElemnent(xpath='//input', name="search")
...
>>> google = GoogleHomePage(
... webdriver=selenium,
... url='http://google.com'
... )
>>> google.go()
>>> google.current_url
'https://www.google.com'
>>> google.search_bar.send_keys('pom-elements is cool!')
Or a section of a page as an individual PageObject.
>>> class Header(PageObject):
... dropdown = XPathElement(xpath'//button', html_class="btn btn-secondary dropdown-toggle")
...
>>> header = Header(webriver=webdriver)
>>> header.dropdown.click()
Putting it all togher allows organizing entire pages with PageObjects and PageElements…
>>> class PageHeader(PageObject):
... dropdown = MultiElement(css="div > a")
...
>>> class PageFooter(PageObject):
... logo = XPath(xpath='//img[dat-attrib="best_image"]')
...
>>> class MyPage(PageObject):
... header = PageHeader()
... footer = PageFooter()
…Set the webdriver on the Parent PageObject instance…
>>> mypage = MyPage(webdriver=selenium)
...
>>> mypage.header.dropdown.is_visible()
True
…and access the webdriver on all instances of the PageObject or Element set on them.
>>> mypage.footer.click()
Example - HTML Elements
The pom_elements.html
module is both a way to interact with all html dom elements, as
well a proof of concept for how one could organize classes for their own projects. Not
every html element has a direct html Element class, but common selectors do have classes
available in this library.
A common use case is to select attributes that live in a specific html div.
>>> from pom_elements.html import Div
>>> elem = Div(html_id="bar", text="Text that resides in a <div> element.")
>>> elem.xath
'//div[id="bar"][contains(text(), "Text that resides in a <div> element.")]'
>>> elem.is_visible()
True
Features
- Organize your tests to reflect how your web page is desinged (Page Objects).
- Augment your current tests by using through the organization of Pages, PageObjects, or individual Elements.
- Allows test writers to bring their favorite locator of choice (MultiElement).
- Use any number of nested PageObjects without needing to manually hand off a webdriver to each instance.
- Improve the experience of debugging through geting the Element on every call (Descriptor Protocol).
Expand source code
"""The Python package `pom-elements` for a Page Object Model that extends to Page Elements.
`pom-elements` allows for creating Page Object Models for your testing needs and
extending the model to Elements on the Page. The project is currently only supporting
Selenium webdriver instance, but is intended to help test writers abstract testing
organization around web page development choices.
.. include:: ../documentation.md
"""
from .multi_element import MultiElement
from .page_object import Page, PageObject
from .xpath_element import XPathElement
__all__ = ["MultiElement", "XPathElement", "PageObject", "Page"]
__author__ = "Nick Beaird"
__author_email__ = "nicklasbeaird@gmail.com"
__license__ = "MIT"
__url__ = "https://github.com/nickbeaird/pom-elements"
__version__ = "0.1.1"
Sub-modules
pom_elements.base_element
pom_elements.html
-
The Python package
pom-elements.html
module is for interacting with html Elements … pom_elements.multi_element
pom_elements.page_object
pom_elements.xpath_element
Classes
class MultiElement (webdriver:
= None, timeout: Union[float, NoneType] = 0.5, **kwargs) -
Set any Page Element using a Selenium Locators.
Args
webdriver
:webdriver
- A selenium webdriver instance.
timeout
:float
- An integer or float. Defaults to default timeout if not set.
css
:str
- the string represenation of a css selector. Uses By.CSS_SELECTOR.
id_
:str
- the html id of the page element. Uses By.ID.
class_name
:str
- the html class of the page element. Uses By.NAME.
link_text
:str
- the texts of provided link. Uses By.LINK_TEXT.
partial_link_text
:str
- the partial text of a link. Uses. By.PARTIAL_LINK_TEXT.
tag
:str
- the name of the name. Uses By.TAG_NAME.
xpath
:str
- the xpath query of the page element. Uses. By.XPATH.
Raises
AttributeError
- The MultiClass selector must have one locator type and query.
AttributeError
- The MultiClass cannot have more then one locator type and query.
Expand source code
class MultiElement(BaseElement): """Set any Page Element using a Selenium Locators. Args: webdriver (webdriver): A selenium webdriver instance. timeout (float): An integer or float. Defaults to default timeout if not set. css (str): the string represenation of a css selector. Uses By.CSS_SELECTOR. id_ (str): the html id of the page element. Uses By.ID. class_name (str): the html class of the page element. Uses By.NAME. link_text (str): the texts of provided link. Uses By.LINK_TEXT. partial_link_text (str): the partial text of a link. Uses. By.PARTIAL_LINK_TEXT. tag (str): the name of the name. Uses By.TAG_NAME. xpath (str): the xpath query of the page element. Uses. By.XPATH. Raises: AttributeError: The MultiClass selector must have one locator type and query. AttributeError: The MultiClass cannot have more then one locator type and query. """ LOCATOR = { "css": By.CSS_SELECTOR, "id_": By.ID, "name": By.NAME, "class_name": By.CLASS_NAME, "link_text": By.LINK_TEXT, "partial_link_text": By.PARTIAL_LINK_TEXT, "tag": By.TAG_NAME, "xpath": By.XPATH, } """The dictionary containing the Selenium LOCATOR reference.""" def __init__( self, webdriver: webdriver = None, timeout: Optional[float] = 0.5, **kwargs ) -> None: locators = {k: v for k, v in kwargs.items() if k in self.LOCATOR} if len(locators) == 0 or kwargs is None: raise AttributeError("No attribute was set. Please set a Locator.") if len(locators) > 1: raise AttributeError( "Two attributes set and this can only set a single Locator." ) k, v = next(iter(locators.items())) self._locator = (self.LOCATOR[k], v) super().__init__(webdriver, timeout) @property def locator(self) -> Tuple[By, str]: """Return the locator set on the class.""" return self._locator @locator.setter def locator(self, locator: Union[Tuple[str, str], str]) -> None: """Set the locator for the class.""" raise NotImplementedError( "Changing an Element's locator is not avilable at this time." )
Ancestors
- BaseElement
- abc.ABC
Class variables
var LOCATOR
-
The dictionary containing the Selenium LOCATOR reference.
Instance variables
var default_timeout
-
Inherited from:
BaseElement
.default_timeout
Get the default timeout.
var locator : Tuple[selenium.webdriver.common.by.By, str]
-
Return the locator set on the class.
Expand source code
@property def locator(self) -> Tuple[By, str]: """Return the locator set on the class.""" return self._locator
var webdriver :
-
Inherited from:
BaseElement
.webdriver
Get the Element's Selenium webdriver instance …
Methods
def can_be_clicked(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.can_be_clicked
Return true if the webelement can be clicked in the time (seconds) provided …
def click(self, timeout: Union[float, NoneType] = None)
-
Inherited from:
BaseElement
.click
Click the web element if it is available to be clicked.
def find(self, timeout: Union[float, NoneType] = None) -> selenium.webdriver.remote.webelement.WebElement
-
Inherited from:
BaseElement
.find
Returns the defined Selenium WebElement in the provided timeout or raise an error …
def get_all_attributes(self) -> dict
-
Inherited from:
BaseElement
.get_all_attributes
Return a dictionary containing all of the attributes of an element.
def get_attribute(self, name)
-
Inherited from:
BaseElement
.get_attribute
Returns the attribute of the name provided.
def get_property(self, value: str) -> Any
-
Inherited from:
BaseElement
.get_property
Get the property of the element …
def is_displayed(self)
-
Inherited from:
BaseElement
.is_displayed
Returns true if the Element is displayed else false.
def is_invisible(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.is_invisible
Return true if the webelement is invisible on the page within the time (seconds) provided …
def is_visible(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.is_visible
Return true if the webelement is visible on the page in the time (seconds) provided …
class Page (webdriver:
, url: str) -
The class representing all of the methods and interactions of a web page.
A Page represents all of the functionality of the webdriver that is not encapsulated in the PageElement objects. The attached methods are all interactions with the web browser instance, and not elements on a single web page.
Args
webdriver
:webdriver
- A selenium webdriver instance for the PageObject.
url
:str
- The url of the the PageObject.
Expand source code
class Page(PageObject): """The class representing all of the methods and interactions of a web page. A Page represents all of the functionality of the webdriver that is not encapsulated in the PageElement objects. The attached methods are all interactions with the web browser instance, and not elements on a single web page. Args: webdriver (webdriver): A selenium webdriver instance for the PageObject. url (str): The url of the the PageObject. """ def __init__(self, webdriver: webdriver, url: str) -> None: super().__init__(webdriver=webdriver) self._url = url @property def url(self) -> str: """Get the url defined for this Page Object.""" return self._url @url.setter def url(self, url: str) -> None: """Set the url for this instance. Args: url: The url that this Page should be defined. """ self._url = url @property def current_url(self) -> str: """Return the url of the page currently being displayed by the webdriver.""" return self.webdriver.current_url def go(self) -> None: """Navigate to the url set as the url for this Page object.""" self.webdriver.get(self.url) def get(self, url: str) -> None: """Navigate to the provided url. Args: url: A url as a string. """ self.webdriver.get(url) def back(self) -> None: """Navigate back one page on the web browser.""" self.webdriver.back() def forward(self) -> None: """Navigate forward one page on the web browser.""" self.webdriver.forward() def refresh(self) -> None: """Refresh the web browser.""" self.webdriver.refresh() @property def title(self) -> str: """The title of the current web page.""" return self.webdriver.title def quit(self) -> None: """Quit the webdriver instance.""" self.webdriver.quit() def maximize_window(self) -> None: """Maximize the web browser's window.""" self.webdriver.maximize_window() def minimize_window(self) -> None: """Minimize the web browser's window.""" self.webdriver.minimize_window()
Ancestors
Instance variables
var current_url : str
-
Return the url of the page currently being displayed by the webdriver.
Expand source code
@property def current_url(self) -> str: """Return the url of the page currently being displayed by the webdriver.""" return self.webdriver.current_url
var title : str
-
The title of the current web page.
Expand source code
@property def title(self) -> str: """The title of the current web page.""" return self.webdriver.title
var url : str
-
Get the url defined for this Page Object.
Expand source code
@property def url(self) -> str: """Get the url defined for this Page Object.""" return self._url
Methods
def back(self) -> NoneType
-
Navigate back one page on the web browser.
Expand source code
def back(self) -> None: """Navigate back one page on the web browser.""" self.webdriver.back()
def forward(self) -> NoneType
-
Navigate forward one page on the web browser.
Expand source code
def forward(self) -> None: """Navigate forward one page on the web browser.""" self.webdriver.forward()
def get(self, url: str) -> NoneType
-
Navigate to the provided url.
Args
url
- A url as a string.
Expand source code
def get(self, url: str) -> None: """Navigate to the provided url. Args: url: A url as a string. """ self.webdriver.get(url)
def go(self) -> NoneType
-
Navigate to the url set as the url for this Page object.
Expand source code
def go(self) -> None: """Navigate to the url set as the url for this Page object.""" self.webdriver.get(self.url)
def maximize_window(self) -> NoneType
-
Maximize the web browser's window.
Expand source code
def maximize_window(self) -> None: """Maximize the web browser's window.""" self.webdriver.maximize_window()
def minimize_window(self) -> NoneType
-
Minimize the web browser's window.
Expand source code
def minimize_window(self) -> None: """Minimize the web browser's window.""" self.webdriver.minimize_window()
def quit(self) -> NoneType
-
Quit the webdriver instance.
Expand source code
def quit(self) -> None: """Quit the webdriver instance.""" self.webdriver.quit()
def refresh(self) -> NoneType
-
Refresh the web browser.
Expand source code
def refresh(self) -> None: """Refresh the web browser.""" self.webdriver.refresh()
class PageObject (webdriver:
= None) -
Class that allows for easily outlining your web pages as classes.
Todo
Add type hints for the Selenium library to add Optional[webdriver].
Args
webdriver
:webdriver
- A selenium webdriver instance for the PageObject.
Expand source code
class PageObject: """Class that allows for easily outlining your web pages as classes. Todo: Add type hints for the Selenium library to add Optional[webdriver]. Args: webdriver (webdriver): A selenium webdriver instance for the PageObject. """ def __init__(self, webdriver: webdriver = None) -> None: if webdriver is not None: self.webdriver = webdriver def __get__(self, instance, owner): """Return a PageObject and set the webdriver to the webdriver of the parent PageObject instance. The PageObject class allows users to create any number of PageObject instances as class variables on a PageObject class and pass the webdriver from parent PageObject class to itself. Additionally, we are propogating the webdriver as instance variables rather than class variables, as this allows us to create any number of PageObject instances under a single PageObject representing the Page and use separate webdrivers. For example, one test can verify a PageObject with a WebDriver.session_id for Chrome, while a separate instantiated PageObject Page can have a WebDriver.session_id for Firefox, Safari, or any other browser as available with Selenium. """ instance_webdriver = getattr(instance, "webdriver", None) if instance_webdriver is not None: self.webdriver = instance_webdriver return self
Subclasses
class XPathElement (webdriver:
= None, timeout: int = 5, xpath: str = '', **kwargs) -
Generate xpath query and return Selenium WebElements as Page Elements.
Args
webdriver
:webdriver
- A Selenium webdriver instance.
timeout
:float
- An integer or float setting the default timeout for all XPath methods.
xpath
:str
- The xpath can be directly set, or is auto-generated by the tag class variable, and attrbutes provided.
text
:str
- Searches the provded Element for text that contains this value.
html_INPUT
:str
- Adds the arg with the html_ removed from this. Useful for class and id.
INPUT_contains
:str
- Add an xpath query to search for attributes that contain the value listed.
Examples
>>> print(XPath(html_id="foo").xpath) '//*[@id="foo"]'
>>> print(XPath(xpath='//div/span', data-attrbite='bar', name='baz').xpath) '//div/span[@data-attribute="bar"][@name="baz"]'
>>> print(XPath(class_contains="foo").xpath) '//*[contains(@class, "foo")]'
Expand source code
class XPathElement(BaseElement): """Generate xpath query and return Selenium WebElements as Page Elements. Args: webdriver (webdriver): A Selenium webdriver instance. timeout (float): An integer or float setting the default timeout for all XPath methods. xpath (str): The xpath can be directly set, or is auto-generated by the tag class variable, and attrbutes provided. text (str): Searches the provded Element for text that contains this value. html_INPUT (str): Adds the arg with the html_ removed from this. Useful for class and id. INPUT_contains (str): Add an xpath query to search for attributes that contain the value listed. Examples: >>> print(XPath(html_id="foo").xpath) '//*[@id="foo"]' >>> print(XPath(xpath='//div/span', data-attrbite='bar', name='baz').xpath) '//div/span[@data-attribute="bar"][@name="baz"]' >>> print(XPath(class_contains="foo").xpath) '//*[contains(@class, "foo")]' """ tag = "*" """tag class attribute is the xpath html tag (i.e. div, span, ul).""" def __init__( self, webdriver: webdriver = None, timeout: int = 5, xpath: str = "", **kwargs, ) -> None: self.user_input = kwargs self.xpath = xpath super().__init__(webdriver, timeout) @property def locator(self) -> Tuple[By, str]: """Return the Xpath Locator element.""" return (By.XPATH, self.xpath) def find(self, timeout: Optional[float] = None) -> WebElement: """Return the Selenium WebElement in the provided timeout or raise an error. Args: timeout: The length of time that we expect a WebElement to be returned within. Defaults to the _default_timeout if not set. """ if timeout is None: timeout = self.default_timeout try: wait = WebDriverWait(self.webdriver, timeout) elem = wait.until(EC.presence_of_element_located(self.locator)) except TimeoutException as exc: raise AssertionError( f"Unable to find {str(self.__repr__())}, xpath: {self.xpath} in the {timeout} seconds." ) from exc self.web_element = elem return elem @property def xpath(self) -> str: """Get the xpath value as a string.""" return self._xpath @xpath.setter def xpath(self, xpath: str) -> None: # noqa: C901 """Set the xpath value of the string as value. Args: xpath: The xpath of the page element. """ if not isinstance(xpath, str): raise ValueError("xpath must be of type str") generated_xpath = "" if xpath != "": generated_xpath = xpath else: generated_xpath = f"//{self.tag}" # Parse self.user_input and calculate xpath based on values provided. for key in self.user_input.keys(): if key.startswith("html_"): arg_strip_html = key[5:] generated_xpath += f'[@{arg_strip_html}="{self.user_input[key]}"]' elif key == "text": generated_xpath += f'[contains(text(), "{self.user_input[key]}")]' elif key.endswith("_contains"): arg_in_xpath_args = key[:-9] generated_xpath += ( f'[contains(@{arg_in_xpath_args}, "{self.user_input[key]}")]' ) else: generated_xpath += f'[@{key}="{self.user_input[key]}"]' self._xpath = generated_xpath def __repr__(self) -> str: """Return __repr__ of XPathElement.""" return f'{self.__class__.__name__}(xpath="{str(self.xpath)}")'
Ancestors
- BaseElement
- abc.ABC
Subclasses
- H1
- H2
- H3
- H4
- H5
- H6
- Button
- DataList
- Fieldset
- Form
- Input
- Label
- Legend
- Meter
- OptGroup
- Option
- Select
- TextArea
- Cite
- Code
- Highlight
- InlineQuote
- Link
- Span
- Subscript
- Superscript
- Area
- Iframe
- Img
- Picture
- Source
- Video
- Caption
- Col
- ColGroup
- Table
- TableData
- TableFooter
- TableHead
- TableHeader
- TableRow
- DD
- DT
- Div
- OL
- P
- Quote
- UL
Class variables
var tag
-
tag class attribute is the xpath html tag (i.e. div, span, ul).
Instance variables
var default_timeout
-
Inherited from:
BaseElement
.default_timeout
Get the default timeout.
var locator : Tuple[selenium.webdriver.common.by.By, str]
-
Return the Xpath Locator element.
Expand source code
@property def locator(self) -> Tuple[By, str]: """Return the Xpath Locator element.""" return (By.XPATH, self.xpath)
var webdriver :
-
Inherited from:
BaseElement
.webdriver
Get the Element's Selenium webdriver instance …
var xpath : str
-
Get the xpath value as a string.
Expand source code
@property def xpath(self) -> str: """Get the xpath value as a string.""" return self._xpath
Methods
def can_be_clicked(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.can_be_clicked
Return true if the webelement can be clicked in the time (seconds) provided …
def click(self, timeout: Union[float, NoneType] = None)
-
Inherited from:
BaseElement
.click
Click the web element if it is available to be clicked.
def find(self, timeout: Union[float, NoneType] = None) -> selenium.webdriver.remote.webelement.WebElement
-
Return the Selenium WebElement in the provided timeout or raise an error.
Args
timeout
- The length of time that we expect a WebElement to be returned within.
Defaults to the _default_timeout if not set.
Expand source code
def find(self, timeout: Optional[float] = None) -> WebElement: """Return the Selenium WebElement in the provided timeout or raise an error. Args: timeout: The length of time that we expect a WebElement to be returned within. Defaults to the _default_timeout if not set. """ if timeout is None: timeout = self.default_timeout try: wait = WebDriverWait(self.webdriver, timeout) elem = wait.until(EC.presence_of_element_located(self.locator)) except TimeoutException as exc: raise AssertionError( f"Unable to find {str(self.__repr__())}, xpath: {self.xpath} in the {timeout} seconds." ) from exc self.web_element = elem return elem
def get_all_attributes(self) -> dict
-
Inherited from:
BaseElement
.get_all_attributes
Return a dictionary containing all of the attributes of an element.
def get_attribute(self, name)
-
Inherited from:
BaseElement
.get_attribute
Returns the attribute of the name provided.
def get_property(self, value: str) -> Any
-
Inherited from:
BaseElement
.get_property
Get the property of the element …
def is_displayed(self)
-
Inherited from:
BaseElement
.is_displayed
Returns true if the Element is displayed else false.
def is_invisible(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.is_invisible
Return true if the webelement is invisible on the page within the time (seconds) provided …
def is_visible(self, timeout: Union[int, NoneType] = None) -> bool
-
Inherited from:
BaseElement
.is_visible
Return true if the webelement is visible on the page in the time (seconds) provided …