manen.page_object_model#

This module provides an implementation of the Page Object design pattern described in Selenium documentation. By combining the classes Page and Region with the Element object (and all its subclasses), you can easily describe any web pages and access all the DOM elements in a simple way through a Python class.

Let’s say that you want, given a query, to get all the packages information from the website PyPi (e.g.: “selenium”). The first step to work with manen.page_object_model is to define the Python classes which will describe the web pages you are working on. Here we will define all the classes in an external file called pypi_pom.py.

from manen.page_object_model import (Page, Regions, InputElement,
                                     TextElement, LinkElement,
                                     IntegerElement)

class HomePage(Page):
    query = InputElement("input[id='search']")

class SearchResultPage(Page):
    class ResultRegions(Regions):
        name = TextElement("h3 span.package-snippet__name")
        version = TextElement("h3 span.package-snippet__version")
        link = LinkElement("a.package-snippet")
        description = TextElement("p.package-snippet__description")

    n_results = IntegerElement("//*[@id='content']//form/div[1]/div[1]/p/strong")
    results = ResultRegions("ul[aria-label='Search results'] li")

Once you have defined all the classes describing the web pages, you can start interacting by instanciating the Page subclass with an instance of WebDriver. Here we will suppose that you have an instance of WebDriver stored in the variable browser.

>>> from pypi_pom import HomePage, SearchResultPage
>>> home_page = HomePage(browser)
>>> home_page.query = "selenium"
>>> home_page.query = Action("submit")
# This will direct you to a search result page of PyPi.
>>> browser.current_url
"https://pypi.org/search/?q=selenium"
>>> page = SearchResultPage(browser)
>>> page.n_results
1600
>>> len(page.results)
20
>>> page.results[0]
<pypi_pom.ResultRegions>
>>> page.results[0].name
"selenium"
>>> page.results[0].version
"3.141.0"
>>> page.results[0].link
"https://pypi.org/project/selenium/"

This is a glitch of what you can do with manen.page_object_model. See the documentation of each objects to check all the features provided by the module.

class manen.page_object_model.Action(method: str, *args, **kwargs)[source]#

Bases: object

Enable to interact with an input by calling a method of the WebElement. This is intended to be used by a subtype of Element which can be set.

Example:

>>> page.query = "python manen"
>>> page.query = Action("submit")
__dict__ = mappingproxy({'__module__': 'manen.page_object_model', '__doc__': 'Enable to interact with an input by calling a method of the WebElement. This is\n    intended to be used by a subtype of :py:class:`~manen.page_object_model.Element`\n    which can be set.\n\n    Example::\n\n        >>> page.query = "python manen"\n        >>> page.query = Action("submit")\n    ', '__init__': <function Action.__init__>, '__dict__': <attribute '__dict__' of 'Action' objects>, '__weakref__': <attribute '__weakref__' of 'Action' objects>, '__annotations__': {}})#
__init__(method: str, *args, **kwargs)[source]#
Parameters:
  • method (str) – name of the method to call. This should be a method ones of a method of selenium.webdriver.remote.webelement.WebElement

  • *args (Any) – positional arguments called with the method

  • **kwargs – keyword arguments called with the method

__module__ = 'manen.page_object_model'#
__weakref__#

list of weak references to the object (if defined)

class manen.page_object_model.CheckboxElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Get the status of a checkbox element directly as a boolan. Setting a boolean value to a checkbox element will directly change the value of the DOM element.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
__set__(area: WebArea, value: bool)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.DOMAccessor(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: object

Main interface of a DOM element.

__annotations__ = {'_post_processing': typing.List[ForwardRef('PostProcessingFunction')]}#
__dict__ = mappingproxy({'__module__': 'manen.page_object_model', '__annotations__': {'_post_processing': typing.List[ForwardRef('PostProcessingFunction')], '_name': 'str', '_path': 'Tuple[str, ...]'}, '__doc__': 'Main interface of a DOM element.', '_post_processing': [], '_many': False, '__init_subclass__': <classmethod(<function DOMAccessor.__init_subclass__>)>, '__init__': <function DOMAccessor.__init__>, '__set_name__': <function DOMAccessor.__set_name__>, '_apply_post_processing': <function DOMAccessor._apply_post_processing>, '_get_from': <function DOMAccessor._get_from>, '_build_elements': <function DOMAccessor._build_elements>, '__dict__': <attribute '__dict__' of 'DOMAccessor' objects>, '__weakref__': <attribute '__weakref__' of 'DOMAccessor' objects>})#
__init__(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Main interface with a DOM element.

Parameters:
  • selectors (Union[str, List[str]]) – Selector or sets of selectors used to identify the DOM element(s). Each selector must be in form of {selection_method}:{selector} where {selection_method} is in …. Note that if no selection method is specified, it will be inferred from the selector (but only the CSS and XPath method can be inferred).

  • default (Any, optional) – Default value to returned if no element matching the selector(s) is found. Defaults to NotImplemented.

  • wait (int, optional) – Time to wait before throwing an error. Defaults to 0.

  • post_processing (Callable[[Any], Any]) – Callable called after getting the element from the DOM. Defaults to None.

classmethod __init_subclass__(many: bool | None = None, post_processing: List[PostProcessingFunction] | None = None)[source]#

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__module__ = 'manen.page_object_model'#
__set_name__(owner, name: str)[source]#
__weakref__#

list of weak references to the object (if defined)

_apply_post_processing(element: WebElement) Any[source]#
_build_elements(elements, context, area) WebArea | List[WebArea][source]#
_get_from(area: WebArea, area_class) Any[source]#
_many = False#
_post_processing: List[PostProcessingFunction] = []#
class manen.page_object_model.DateTimeElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: TextElement

Extract a datetime from a text element matching a set of selectors.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>, <function parse>]#
class manen.page_object_model.DateTimeElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: DateTimeElement

Pluralized version of DateTimeElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>, <function parse>]#
class manen.page_object_model.Element(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: DOMAccessor

Based on one or several selectors, extract a selenium.webdriver.remote.webelement.WebElement from a region or page.

An element should be used to initialize a class attribute of a subclass of Page, Region or Frame (or any subtypes of WebArea)

__annotations__ = {}#
__get__(area: WebArea, area_cls: type) WebElement[source]#
__module__ = 'manen.page_object_model'#
__set__(area: WebArea, value: Any)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = []#
classmethod _yaml_loader(loader: Loader, node: Node)[source]#
class manen.page_object_model.Elements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Pluralized version of Element.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = []#
class manen.page_object_model.Frame(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: DOMAccessor

Enable to work with <iframe> element.

__annotations__ = {}#
__get__(area, area_cls) WebArea[source]#
__module__ = 'manen.page_object_model'#
__set__(instance, value)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = []#
class manen.page_object_model.IgnorePageObjectLoader(*args, **kwargs)[source]#

Bases: Loader

Loader used to build page from a YAML file. This loader automatically registers all classes in the current module having a method _yaml_loader.

__init__(*args, **kwargs)[source]#

Initialize the scanner.

__module__ = 'manen.page_object_model'#
ignore(loader: Loader, node: Node) Any[source]#

Prevent loading a YAML file with special classes when a tag is specified. This is useful when you want to a Manen YAML page without loading the element but only a regular Python object.

Parameters:
  • loader (yaml.Loader) – instance of YAML Loader which describe how to build an element.

  • node (yaml.Node) – node to construct

Raises:

TypeError – Raised if the special tag is set on something different from a SequenceNode or a MappingNode

Returns:

Python object as it should be loaded with a regular YAML

Loader

Return type:

Any

class manen.page_object_model.ImageSourceElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Extract the source URL of an image.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.ImageSourceElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: ImageSourceElement

Pluralized version of ImageSourceElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.InnerHtmlElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Extract the inner HTML from an element.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.InnerHtmlElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: InnerHtmlElement

Pluralized version of manen.page_object_model.InnerHtmlElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.InputElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Interface with an <input> element of a DOM. Setting the variable of the InputElement will also set the value of the DOM.

Example:

>>> from manen.page_object_model import Page, InputElement
>>> class LoginPage(Page):
...     email = InputElement("input[type='email']")
...     password = InputElement("input[type='password']")

>>> from manen.page_object_model import Action
>>> login_page = LoginPage(browser)
>>> login_page.email = "username@manen.co"
>>> login_page.password = "dummypassword"
>>> login_page.password = Action("submit")
__annotations__ = {}#
__module__ = 'manen.page_object_model'#
__set__(area: WebArea, value: Any)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.IntegerElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: TextElement

Extract the integer from a text element matching a selector or set of selectors.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>, <function extract_integer>]#
class manen.page_object_model.IntegerElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: IntegerElement

Get elements from a HTML page matching a set of selectors and extract a integer from each text element. Pluralized version of IntegerElement

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>, <function extract_integer>]#
class manen.page_object_model.LinkElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Extract the link from an element matching one or several selectors.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.LinkElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: LinkElement

Pluralized version of LinkElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.OuterHtmlElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Extract the outer HTML of an element.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.OuterHtmlElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: OuterHtmlElement

Pluralized version of OuterHtmlElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.Page(container: SeleniumElement, _context: str = 'PAGE')[source]#

Bases: WebArea

Modelize a HTML page in the Page Object Model design pattern. The attributes of Page should be an instance of Element (or any subclass) or an instance of Region.

Example:

>>> class QueryPage(Page):
...     main_title = TextElement("h1")
...     search = InputElement("input[name='query']")
...     button_validate = Element("button#validate")
>>> page = QueryPage(browser)
>>> page.search = "python manen"
>>> button_validate.click()
__module__ = 'manen.page_object_model'#
click_with_js(element: WebElement)[source]#

Click on an element using a JavaScript script.

classmethod from_object(page_objects: Dict[str, Element], bases: Tuple = (), name: str = 'Page')[source]#

Build a Page class from a dictionary.

classmethod from_yaml(filepath: str | Path)[source]#

Build a Page class from a YAML file.

open(**kwargs)[source]#

Go to the URL specified in the Meta class associated to a Page. Any keywords arguments will be passed to the URL to format it.

property page_source: str#

Code source of the page.

property title: str#

Title of the page.

class manen.page_object_model.PageObjectLoader(*args, **kwargs)[source]#

Bases: Loader

Loader used to build page from a YAML file. This loader automatically registers all classes in the current module having a method _yaml_loader.

__annotations__ = {}#
__init__(*args, **kwargs)[source]#

Initialize the scanner.

__module__ = 'manen.page_object_model'#
class manen.page_object_model.RadioButtonElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Mapper to a set of choices controlled by a radio element in the page. Setting the value of the radio button element will directly update the DOM element with the specified value.

__annotations__ = {}#
__get__(area, area_class)[source]#
__module__ = 'manen.page_object_model'#
__set__(area: WebArea, value: bool)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = []#
class manen.page_object_model.Region(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: DOMAccessor

Modelize a region of a webpage. A region is a set of elements contained in the same container.

Note

Contrary to Page, this class should not be instanciated with an instance of WebDriver. This class is closer to an Element than a Element.

Example:

>>> from manen.page_object_model import Page, Region, InputElement, Action
>>> class LoginPage(Page):
...     class FormRegion(Region):
...         email = InputElement("input#email")
...         password = InputElement("input#password")
...     form_region = FormRegion("div#form-container")
>>> page = LoginPage(browser)
>>> page.form_region.email = "hello@manen.com"
>>> page.form_region.password = "strong_password"
>>> page.form_region.password = Action("submit")
__annotations__ = {}#
__get__(area: WebArea, area_cls) WebArea | List[WebArea][source]#
__module__ = 'manen.page_object_model'#
__set__(area: WebArea, value: Any)[source]#
_many = False#
_post_processing: List['PostProcessingFunction'] = []#
classmethod _yaml_loader(loader: Loader, node: Node)[source]#
class manen.page_object_model.Regions(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Region

Pluralized version of Region.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = []#
class manen.page_object_model.SelectElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

A shortcut to get an instance of Select for a select element. See guide to work with Select element from official documentation. # pylint: disable=line-too-long

__annotations__ = {}#
__get__(area, area_class)[source]#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = []#
class manen.page_object_model.TextElement(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: Element

Extract text from an element matching one or several selectors.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = False#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.TextElements(selectors: str | List[str] | None = None, *, default: Any = NotImplemented, wait: int = 0, post_processing: PostProcessingFunction | None = None)[source]#

Bases: TextElement

Pluralized version of TextElement.

__annotations__ = {}#
__module__ = 'manen.page_object_model'#
_many = True#
_post_processing: List['PostProcessingFunction'] = [<function <lambda>>]#
class manen.page_object_model.WebArea(container: SeleniumElement, _context: str = 'PAGE')[source]#

Bases: object

Wrapper around a Selenium element which works together with an Element (and its subclasses). Any class attribute instanciated with Element will be accessible as an instance attribute; it will, when accessed, evaluate the selector to retrieve the element.

Example:

>>> class Area(WebArea):
...    body = Element("css:body")
...    first_div = Element("css:div")
...    links = Elements("css:a")
>>> area = Area(browser)
>>> area.body
<selenium.webdriver.remote.webelement.WebElement id="abcdef-1234">
>>> div_area = Area(area.first_div)
>>> div_area.links # Find all the links in the first div element of the body

Note

This class is mainly used internally to build the classes Page, Region and Frame. You shouldn’t need to use this class to create basic page objects (see the documentation of each object to see how to efficiently used page object modelling).

class Meta[source]#

Bases: object

Metadata for a webarea.

__annotations__ = {'selectors': typing.Dict[str, typing.Any], 'selectors_path': <class 'str'>, 'url': typing.Optional[str]}#
__dict__ = mappingproxy({'__module__': 'manen.page_object_model', '__annotations__': {'selectors_path': <class 'str'>, 'selectors': typing.Dict[str, typing.Any], 'url': typing.Optional[str]}, '__doc__': 'Metadata for a webarea.', 'selectors_path': '', 'selectors': {}, 'url': None, '__dict__': <attribute '__dict__' of 'Meta' objects>, '__weakref__': <attribute '__weakref__' of 'Meta' objects>})#
__module__ = 'manen.page_object_model'#
__weakref__#

list of weak references to the object (if defined)

selectors: Dict[str, Any] = {}#
selectors_path: str = ''#
url: str | None = None#
__annotations__ = {}#
__dict__ = mappingproxy({'__module__': 'manen.page_object_model', '__doc__': 'Wrapper around a Selenium element which works together with an\n    :py:class:`~manen.page_object_model.Element` (and its subclasses). Any class\n    attribute instanciated with :py:class:`~manen.page_object_model.Element`\n    will be accessible as an instance attribute; it will, when accessed, evaluate\n    the selector to retrieve the element.\n\n    Example::\n\n        >>> class Area(WebArea):\n        ...    body = Element("css:body")\n        ...    first_div = Element("css:div")\n        ...    links = Elements("css:a")\n        >>> area = Area(browser)\n        >>> area.body\n        <selenium.webdriver.remote.webelement.WebElement id="abcdef-1234">\n        >>> div_area = Area(area.first_div)\n        >>> div_area.links # Find all the links in the first div element of the body\n\n    .. note:: This class is mainly used internally to build the classes\n      :py:class:`~manen.page_object_model.Page`,\n      :py:class:`~manen.page_object_model.Region` and\n      :py:class:`~manen.page_object_model.Frame`. You shouldn\'t need to use this\n      class to create basic page objects (see the documentation of each object to\n      see how to efficiently used page object modelling).\n    ', 'Meta': <class 'manen.page_object_model.WebArea.Meta'>, '__init__': <function WebArea.__init__>, 'container': <property object>, 'switch_container': <function WebArea.switch_container>, '_selectors_from_meta': <classmethod(<function WebArea._selectors_from_meta>)>, '__dict__': <attribute '__dict__' of 'WebArea' objects>, '__weakref__': <attribute '__weakref__' of 'WebArea' objects>, '__annotations__': {}})#
__init__(container: SeleniumElement, _context: str = 'PAGE')[source]#
Parameters:

container ("SeleniumElement") – Parent element in which all the elements will be searched.

__module__ = 'manen.page_object_model'#
__weakref__#

list of weak references to the object (if defined)

classmethod _selectors_from_meta(name)[source]#
property container: SeleniumElement#

Selenium element where to perform the query in order to retrieve some elements with selectors.

Warning

Prefer using the context manager switch_container() rather than this property.

switch_container()[source]#

Context manager which returns the container where to perform the query. In most cases, it is the container used to instanciate WebArea but some additional computing may be needed in some cases (for example, when working with frame element). The additional computation will occur based on the value of the attribute _context, entered at instanciation.

Yields:

container (selenium.webdriver.remote.webelement.WebElement) – Selenium element where to evaluate a selector

manen.page_object_model.load_selector_config(path: str, to_element=False) Dict[str, Any][source]#

Load the Manen YAML page from a specified path. You can choose whether or not the element of a Manen page should return Manen elements or just the a regular Python object.

Parameters:
  • path (str) – filepath of the Manen YAML page

  • to_element (bool, optional) – whether or not to return Manen elements if custom tags are specified in the YAML. Defaults to False.

Returns:

Manen page loaded a Python mapping

Return type:

Dict[str, Any]