[![PyPI Version](https://img.shields.io/pypi/v/pypom-selenium)
![PyPI - Downloads](https://img.shields.io/pypi/dm/pypom-selenium)
![PyPI - License](https://img.shields.io/pypi/l/pypom-selenium)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pypom-selenium)](https://pypi.python.org/pypi/pypom-selenium)
# PyPOM Selenium
PyPOM Selenium is a Python Page Object Model library for Selenium. It presents a thin interface for implementing the page object model design pattern. The project is built based on [mozilla/PyPOM](https://github.com/mozilla/PyPOM), all credit goes to the authors.
This project is licensed under the Mozilla Public License version 2.0.
## Table of Contents
- [Installation](#installation)
- [User Guide](#user-guide)
- [Drivers](#drivers)
- [Pages](#pages)
- [Base URL](#base-url)
- [URL templates](#url-templates)
- [URL parameters](#url-parameters)
- [Waiting for pages to load](#waiting-for-pages-to-load)
- [Regions](#regions)
- [Root elements](#root-elements)
- [Repeating regions](#repeating-regions)
- [Nested regions](#nested-regions)
- [Shared regions](#shared-regions)
- [Waiting for regions to load](#waiting-for-regions-to-load)
- [Type hints](#type-hints)
- [Narrowing the type of a Region class](#narrowing-the-type-of-a-region-class)
- [Development](#development)
- [Running tests](#running-tests)
- [Release Notes](#release-notes)
- [2.0.0](#200-2024-08-15)
- [1.1.0](#110-2024-08-07)
- [1.0.0](#100-2024-08-06)
## Installation
PyPOM Selenium requires Python >= 3.8
To install PyPOM Selenium using [pip](https://pip.pypa.io/)
```sh
pip install pypom-selenium
```
## User Guide
### Drivers
PyPOM Selenium requires a driver object to be instantiated, and supports multiple driver types. The examples in this guide will assume that you have a driver instance.
To instantiate a Selenium driver you will need a `~selenium.webdriver.remote.webdriver.WebDriver` object
```py
from selenium.webdriver import Firefox
driver = Firefox()
```
### Pages
Page objects are representations of web pages. They provide functions to allow simulating user actions, and providing properties that return state from the page. The `~pypom_selenium.page.Page` class provided by PyPOM provides a simple implementation that can be sub-classed to apply to your project.
The following very simple example instantiates a page object representing the landing page of the Mozilla website
```py
from pypom_selenium import Page
class Mozilla(Page):
pass
page = Mozilla(driver)
```
If a page has a seed URL then you can call the `~pypom_selenium.page.Page.open` function to open the page in the browser. There are a number of ways to specify a seed URL.
#### Base URL
A base URL can be passed to a page object on instantiation. If no URL template is provided, then calling `~pypom_selenium.page.Page.open` will open this base URL
```py
from pypom_selenium import Page
class Mozilla(Page):
pass
base_url = 'https://www.mozilla.org'
page = Mozilla(driver, base_url).open()
```
#### URL templates
By setting a value for `~pypom_selenium.page.Page.URL_TEMPLATE`, pages can specify either an absolute URL or one that is relative to the base URL (when provided). In the following example, the URL https://www.mozilla.org/about/ will be opened
```py
from pypom_selenium import Page
class Mozilla(Page):
URL_TEMPLATE = '/about/'
base_url = 'https://www.mozilla.org'
page = Mozilla(driver, base_url).open()
```
As this is a template, any additional keyword arguments passed when instantiating the page object will attempt to resolve any placeholders. In the following example, the URL https://www.mozilla.org/de/about/ will be opened
```py
from pypom_selenium import Page
class Mozilla(Page):
URL_TEMPLATE = '/{locale}/about/'
base_url = 'https://www.mozilla.org'
page = Mozilla(driver, base_url, locale='de').open()
```
#### URL parameters
Any keyword arguments provided that are not used as placeholders in the URL template are added as query string parameters. In the following example, the URL https://developer.mozilla.org/fr/search?q=bold&topic=css will be opened
```py
from pypom_selenium import Page
class Search(Page):
URL_TEMPLATE = '/{locale}/search'
base_url = 'https://developer.mozilla.org/'
page = Search(driver, base_url, locale='fr', q='bold', topic='css').open()
```
#### Waiting for pages to load
Whenever a driver detects that a page is loading, it does its best to block until it's complete. Unfortunately, as the driver does not know your application, it's quite common for it to return earlier than a user would consider the page to be ready. For this reason, the `~pypom_selenium.page.Page.loaded` property can be overridden and customised for your project's needs by returning `True` when the page has loaded. This property is polled by `~pypom_selenium.page.Page.wait_for_page_to_load`, which is called by `~pypom_selenium.page.Page.open` after loading the seed URL, and can be called directly by functions that cause a page to load.
The following example waits for the seed URL to be in the current URL. You can use this so long as the URL is not rewritten or redirected by your application
```py
from pypom_selenium import Page
class Mozilla(Page):
@property
def loaded(self):
return self.seed_url in self.selenium.current_url
```
Other things to wait for might include when elements are displayed or enabled, or when an element has a particular class. This will be very dependent on your application.
### Regions
Region objects represent one or more elements of a web page that are repeated multiple times on a page, or shared between multiple web pages. They prevent duplication, and can improve the readability and maintainability of your page objects.
#### Root elements
It's important for page regions to have a root element. This is the element that any child elements will be located within. This means that page region locators do not need to be unique on the page, only unique within the context of the root element.
If your page region contains a `~pypom_selenium.region.Region._root_locator` attribute, this will be used to locate the root element every time an instance of the region is created. This is recommended for most page regions as it avoids issues when the root element becomes stale.
Alternatively, you can locate the root element yourself and pass it to the region on construction. This is useful when creating regions that are repeated on a single page.
The root element can later be accessed via the `~pypom_selenium.region.Region.root` attribute on the region, which may be necessary if you need to interact with it.
#### Repeating regions
Page regions are useful when you have multiple items on a page that share the same characteristics, such as a list of search results. By creating a page region, you can interact with any of these items in a common way:
The following example uses Selenium to locate all results on a page and return a list of `Result` regions. This can be used to determine the number of results, and each result can be accessed from this list for further state or interactions.
```html
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Repeated Regions Example</h1>
<ol>
<li class="result">
<span class="name">Result 1</span>
<a href="./detail/1/">detail</a>
</li>
<li class="result">
<span class="name">Result 2</span>
<a href="./detail/2/">detail</a>
</li>
<li class="result">
<span class="name">Result 3</span>
<a href="./detail/3/">detail</a>
</li>
<li class="result">
<span class="name">Result 4</span>
<a href="./detail/4/">detail</a>
</li>
</ol>
</body>
</html>
```
```py
from pypom_selenium import Page, Region
from selenium.webdriver.common.by import By
class Results(Page):
_result_locator = (By.CLASS_NAME, "result")
@property
def results(self):
return [
self.Result(self, el) for el in self.find_elements(*self._result_locator)
]
class Result(Region):
_name_locator = (By.CLASS_NAME, "name")
_detail_locator = (By.TAG_NAME, "a")
@property
def name(self):
return self.find_element(*self._name_locator).text
@property
def detail_link(self):
return self.find_element(*self._detail_locator).get_property("href")
```
#### Nested regions
Regions can be nested inside other regions (i.e. a menu region with multiple entry regions). In the following example a main page contains two menu regions that include multiple repeated entry regions:
```html
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Nested Regions Example</h1>
<div id="page">Main Page
<div id="menu1" class="menu">
<ol>
<li class="entry">Menu1-Entry1</li>
<li class="entry">Menu1-Entry2</li>
<li class="entry">Menu1-Entry3</li>
<li class="entry">Menu1-Entry4</li>
<li class="entry">Menu1-Entry5</li>
</ol>
</div>
<div id="menu2" class="menu">
<ol>
<li class="entry">Menu2-Entry1</li>
<li class="entry">Menu2-Entry2</li>
<li class="entry">Menu2-Entry3</li>
</ol>
</div>
</div>
</body>
</html>
```
As a region requires a page object to be passed you need to pass `self.page` when instantiating nested regions:
```py
from pypom_selenium import Region, Page
from selenium.webdriver.common.by import By
class MainPage(Page):
@property
def menu1(self):
root = self.find_element(By.ID, "menu1")
return Menu(self, root=root)
@property
def menu2(self):
root = self.find_element(By.ID, "menu2")
return Menu(self, root=root)
class Menu(Region):
@property
def entries(self):
return [
Entry(self.page, item) for item in self.find_elements(*Entry.entry_locator)
]
class Entry(Region):
entry_locator = (By.CLASS_NAME, "entry")
@property
def name(self):
return self.root.text
```
#### Shared regions
Pages with common characteristics can use regions to avoid duplication. Examples of this include page headers, navigation menus, login forms, and footers. These regions can either be defined in a base page object that is inherited by the pages that contain the region, or they can exist in their own module:
In the following example, any page objects that extend `Base` will inherit
the `header` property, and be able to check if it's displayed.
```py
from pypom_selenium import Page, Region
from selenium.webdriver.common.by import By
class Base(Page):
@property
def header(self):
return self.Header(self)
class Header(Region):
_root_locator = (By.ID, 'header')
def is_displayed(self):
return self.root.is_displayed()
```
#### Waiting for regions to load
The `~pypom_selenium.region.Region.loaded` property function can be overridden and customised for your project's needs by returning `True` when the region has loaded to ensure it's ready for interaction. This property is polled by `~pypom_selenium.region.Region.wait_for_region_to_load`, which is called whenever a region is instantiated, and can be called directly by functions that a region to reload.
The following example waits for an element within a page region to be displayed
```py
from pypom_selenium import Region
class Header(Region):
@property
def loaded(self):
return self.root.is_displayed()
```
Other things to wait for might include when elements are displayed or enabled, or when an element has a particular class. This will be very dependent on your application.
### Type hints
#### Narrowing the type of a Region class
Sometimes its useful to access the "parent" page from a region. The Region class is genericly
typed to allow narrowing the type of the Page of which it is connected to.
This allows language servers and linters to infer and restrict the exact Page type used
with a Region class.
In the example below, note `class Form(Region["MyPage"]):`
```py
from pypom_selenium import Region, Page
from selenium.webdriver.common.by import By
class MyPage(Page):
_body_locator = (By.TAG_NAME, "body")
@property
def text(self) -> str:
return self.find_element(*self._body_locator).text
class Form(Region["MyPage"]):
_button_locator = (By.TAG_NAME, "button")
def submit(self) -> None:
self.find_element(*self._button_locator).click()
# language servers and linters now knows the type of self.page.text
self.wait.until(lambda _: "Successfully submitted" in self.page.text)
```
## Development
### Running Tests
You will need Tox installed to run the tests against the supported Python versions.
```sh
$ python -m pip install tox
$ tox
```
## Release Notes
### 2.0.0 (2024-08-15)
- Type hint information is now distributed with the package.
- Accessing Page.seed_url will now raise UsageError if no base URL or URL_TEMPLATE is set. Previously returned None.
- Accessing Region.root will now raise UsageError if Region instance has not been instantiated with a root and there is no _root_locator defined.
Previously returned None. This also applies to indirect access through Region.find_element[s] methods.
- Distributed package now declares explicit dependencies to Selenium and typing_extenstions.
### 1.1.0 (2024-08-07)
Add support for python 3.10.
The project now officially supports versions 3.8, 3.9, 3.11 and 3.12.
### 1.0.0 (2024-08-06)
First official release.
Raw data
{
"_id": null,
"home_page": null,
"name": "pypom-selenium",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "model, object, page, pypom, selenium",
"author": "Albin P\u00e5lsson",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/52/bd/0cacc7a38b7e596290f66343bc958d08f37030447616e3a599aaa45285a2/pypom_selenium-2.0.0.tar.gz",
"platform": null,
"description": "[![PyPI Version](https://img.shields.io/pypi/v/pypom-selenium)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/pypom-selenium)\n![PyPI - License](https://img.shields.io/pypi/l/pypom-selenium)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pypom-selenium)](https://pypi.python.org/pypi/pypom-selenium)\n\n# PyPOM Selenium\nPyPOM Selenium is a Python Page Object Model library for Selenium. It presents a thin interface for implementing the page object model design pattern. The project is built based on [mozilla/PyPOM](https://github.com/mozilla/PyPOM), all credit goes to the authors.\n\nThis project is licensed under the Mozilla Public License version 2.0.\n\n## Table of Contents\n- [Installation](#installation)\n- [User Guide](#user-guide)\n - [Drivers](#drivers)\n - [Pages](#pages)\n - [Base URL](#base-url)\n - [URL templates](#url-templates)\n - [URL parameters](#url-parameters)\n - [Waiting for pages to load](#waiting-for-pages-to-load)\n - [Regions](#regions)\n - [Root elements](#root-elements)\n - [Repeating regions](#repeating-regions)\n - [Nested regions](#nested-regions)\n - [Shared regions](#shared-regions)\n - [Waiting for regions to load](#waiting-for-regions-to-load)\n - [Type hints](#type-hints)\n - [Narrowing the type of a Region class](#narrowing-the-type-of-a-region-class)\n- [Development](#development)\n - [Running tests](#running-tests)\n- [Release Notes](#release-notes)\n - [2.0.0](#200-2024-08-15)\n - [1.1.0](#110-2024-08-07)\n - [1.0.0](#100-2024-08-06)\n\n\n## Installation\nPyPOM Selenium requires Python >= 3.8 \nTo install PyPOM Selenium using [pip](https://pip.pypa.io/)\n```sh\npip install pypom-selenium\n```\n\n## User Guide\n\n### Drivers\nPyPOM Selenium requires a driver object to be instantiated, and supports multiple driver types. The examples in this guide will assume that you have a driver instance.\n\nTo instantiate a Selenium driver you will need a `~selenium.webdriver.remote.webdriver.WebDriver` object\n```py\n from selenium.webdriver import Firefox\n driver = Firefox()\n```\n\n### Pages\nPage objects are representations of web pages. They provide functions to allow simulating user actions, and providing properties that return state from the page. The `~pypom_selenium.page.Page` class provided by PyPOM provides a simple implementation that can be sub-classed to apply to your project.\n\nThe following very simple example instantiates a page object representing the landing page of the Mozilla website\n```py\n from pypom_selenium import Page\n\n class Mozilla(Page):\n pass\n\n page = Mozilla(driver)\n```\nIf a page has a seed URL then you can call the `~pypom_selenium.page.Page.open` function to open the page in the browser. There are a number of ways to specify a seed URL.\n\n#### Base URL\nA base URL can be passed to a page object on instantiation. If no URL template is provided, then calling `~pypom_selenium.page.Page.open` will open this base URL\n```py\n from pypom_selenium import Page\n\n class Mozilla(Page):\n pass\n\n base_url = 'https://www.mozilla.org'\n page = Mozilla(driver, base_url).open()\n```\n\n#### URL templates\nBy setting a value for `~pypom_selenium.page.Page.URL_TEMPLATE`, pages can specify either an absolute URL or one that is relative to the base URL (when provided). In the following example, the URL https://www.mozilla.org/about/ will be opened\n```py\n from pypom_selenium import Page\n\n class Mozilla(Page):\n URL_TEMPLATE = '/about/'\n\n base_url = 'https://www.mozilla.org'\n page = Mozilla(driver, base_url).open()\n```\n\nAs this is a template, any additional keyword arguments passed when instantiating the page object will attempt to resolve any placeholders. In the following example, the URL https://www.mozilla.org/de/about/ will be opened\n```py\n from pypom_selenium import Page\n\n class Mozilla(Page):\n URL_TEMPLATE = '/{locale}/about/'\n\n base_url = 'https://www.mozilla.org'\n page = Mozilla(driver, base_url, locale='de').open()\n```\n\n#### URL parameters\nAny keyword arguments provided that are not used as placeholders in the URL template are added as query string parameters. In the following example, the URL https://developer.mozilla.org/fr/search?q=bold&topic=css will be opened\n```py\n from pypom_selenium import Page\n\n class Search(Page):\n URL_TEMPLATE = '/{locale}/search'\n\n base_url = 'https://developer.mozilla.org/'\n page = Search(driver, base_url, locale='fr', q='bold', topic='css').open()\n```\n\n#### Waiting for pages to load\nWhenever a driver detects that a page is loading, it does its best to block until it's complete. Unfortunately, as the driver does not know your application, it's quite common for it to return earlier than a user would consider the page to be ready. For this reason, the `~pypom_selenium.page.Page.loaded` property can be overridden and customised for your project's needs by returning `True` when the page has loaded. This property is polled by `~pypom_selenium.page.Page.wait_for_page_to_load`, which is called by `~pypom_selenium.page.Page.open` after loading the seed URL, and can be called directly by functions that cause a page to load.\n\nThe following example waits for the seed URL to be in the current URL. You can use this so long as the URL is not rewritten or redirected by your application\n```py\n from pypom_selenium import Page\n\n class Mozilla(Page):\n\n @property\n def loaded(self):\n return self.seed_url in self.selenium.current_url\n```\n\nOther things to wait for might include when elements are displayed or enabled, or when an element has a particular class. This will be very dependent on your application.\n\n### Regions\nRegion objects represent one or more elements of a web page that are repeated multiple times on a page, or shared between multiple web pages. They prevent duplication, and can improve the readability and maintainability of your page objects.\n\n#### Root elements\nIt's important for page regions to have a root element. This is the element that any child elements will be located within. This means that page region locators do not need to be unique on the page, only unique within the context of the root element.\n\nIf your page region contains a `~pypom_selenium.region.Region._root_locator` attribute, this will be used to locate the root element every time an instance of the region is created. This is recommended for most page regions as it avoids issues when the root element becomes stale.\n\nAlternatively, you can locate the root element yourself and pass it to the region on construction. This is useful when creating regions that are repeated on a single page.\n\nThe root element can later be accessed via the `~pypom_selenium.region.Region.root` attribute on the region, which may be necessary if you need to interact with it.\n\n#### Repeating regions\nPage regions are useful when you have multiple items on a page that share the same characteristics, such as a list of search results. By creating a page region, you can interact with any of these items in a common way:\n\nThe following example uses Selenium to locate all results on a page and return a list of `Result` regions. This can be used to determine the number of results, and each result can be accessed from this list for further state or interactions.\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<body>\n<h1>Repeated Regions Example</h1>\n\n<ol>\n <li class=\"result\">\n <span class=\"name\">Result 1</span>\n <a href=\"./detail/1/\">detail</a>\n </li>\n <li class=\"result\">\n <span class=\"name\">Result 2</span>\n <a href=\"./detail/2/\">detail</a>\n </li>\n <li class=\"result\">\n <span class=\"name\">Result 3</span>\n <a href=\"./detail/3/\">detail</a>\n </li>\n <li class=\"result\">\n <span class=\"name\">Result 4</span>\n <a href=\"./detail/4/\">detail</a>\n </li>\n</ol>\n\n</body>\n</html>\n```\n```py\nfrom pypom_selenium import Page, Region\nfrom selenium.webdriver.common.by import By\n\n\nclass Results(Page):\n _result_locator = (By.CLASS_NAME, \"result\")\n\n @property\n def results(self):\n return [\n self.Result(self, el) for el in self.find_elements(*self._result_locator)\n ]\n\n class Result(Region):\n _name_locator = (By.CLASS_NAME, \"name\")\n _detail_locator = (By.TAG_NAME, \"a\")\n\n @property\n def name(self):\n return self.find_element(*self._name_locator).text\n\n @property\n def detail_link(self):\n return self.find_element(*self._detail_locator).get_property(\"href\")\n```\n\n#### Nested regions\nRegions can be nested inside other regions (i.e. a menu region with multiple entry regions). In the following example a main page contains two menu regions that include multiple repeated entry regions:\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<body>\n<h1>Nested Regions Example</h1>\n<div id=\"page\">Main Page\n\n <div id=\"menu1\" class=\"menu\">\n <ol>\n <li class=\"entry\">Menu1-Entry1</li>\n <li class=\"entry\">Menu1-Entry2</li>\n <li class=\"entry\">Menu1-Entry3</li>\n <li class=\"entry\">Menu1-Entry4</li>\n <li class=\"entry\">Menu1-Entry5</li>\n </ol>\n </div>\n <div id=\"menu2\" class=\"menu\">\n <ol>\n <li class=\"entry\">Menu2-Entry1</li>\n <li class=\"entry\">Menu2-Entry2</li>\n <li class=\"entry\">Menu2-Entry3</li>\n </ol>\n </div>\n</div>\n</body>\n</html>\n```\n\nAs a region requires a page object to be passed you need to pass `self.page` when instantiating nested regions:\n\n```py\nfrom pypom_selenium import Region, Page\nfrom selenium.webdriver.common.by import By\n\n\nclass MainPage(Page):\n @property\n def menu1(self):\n root = self.find_element(By.ID, \"menu1\")\n return Menu(self, root=root)\n\n @property\n def menu2(self):\n root = self.find_element(By.ID, \"menu2\")\n return Menu(self, root=root)\n\n\nclass Menu(Region):\n @property\n def entries(self):\n return [\n Entry(self.page, item) for item in self.find_elements(*Entry.entry_locator)\n ]\n\n\nclass Entry(Region):\n entry_locator = (By.CLASS_NAME, \"entry\")\n\n @property\n def name(self):\n return self.root.text\n```\n\n\n#### Shared regions\nPages with common characteristics can use regions to avoid duplication. Examples of this include page headers, navigation menus, login forms, and footers. These regions can either be defined in a base page object that is inherited by the pages that contain the region, or they can exist in their own module:\n\nIn the following example, any page objects that extend `Base` will inherit\nthe `header` property, and be able to check if it's displayed.\n```py\n from pypom_selenium import Page, Region\n from selenium.webdriver.common.by import By\n\n class Base(Page):\n\n @property\n def header(self):\n return self.Header(self)\n\n class Header(Region):\n _root_locator = (By.ID, 'header')\n\n def is_displayed(self):\n return self.root.is_displayed()\n```\n\n#### Waiting for regions to load\nThe `~pypom_selenium.region.Region.loaded` property function can be overridden and customised for your project's needs by returning `True` when the region has loaded to ensure it's ready for interaction. This property is polled by `~pypom_selenium.region.Region.wait_for_region_to_load`, which is called whenever a region is instantiated, and can be called directly by functions that a region to reload.\n\nThe following example waits for an element within a page region to be displayed\n```py\n from pypom_selenium import Region\n\n class Header(Region):\n\n @property\n def loaded(self):\n return self.root.is_displayed()\n```\n\nOther things to wait for might include when elements are displayed or enabled, or when an element has a particular class. This will be very dependent on your application.\n\n### Type hints\n#### Narrowing the type of a Region class\nSometimes its useful to access the \"parent\" page from a region. The Region class is genericly\ntyped to allow narrowing the type of the Page of which it is connected to.\nThis allows language servers and linters to infer and restrict the exact Page type used\nwith a Region class. \nIn the example below, note `class Form(Region[\"MyPage\"]):`\n```py\n from pypom_selenium import Region, Page\n from selenium.webdriver.common.by import By\n\n class MyPage(Page):\n _body_locator = (By.TAG_NAME, \"body\")\n\n @property\n def text(self) -> str:\n return self.find_element(*self._body_locator).text\n\n class Form(Region[\"MyPage\"]):\n _button_locator = (By.TAG_NAME, \"button\")\n\n def submit(self) -> None:\n self.find_element(*self._button_locator).click()\n # language servers and linters now knows the type of self.page.text\n self.wait.until(lambda _: \"Successfully submitted\" in self.page.text)\n```\n\n\n## Development\n### Running Tests\nYou will need Tox installed to run the tests against the supported Python versions.\n```sh\n$ python -m pip install tox\n$ tox\n```\n\n## Release Notes\n### 2.0.0 (2024-08-15)\n- Type hint information is now distributed with the package.\n- Accessing Page.seed_url will now raise UsageError if no base URL or URL_TEMPLATE is set. Previously returned None.\n- Accessing Region.root will now raise UsageError if Region instance has not been instantiated with a root and there is no _root_locator defined. \n Previously returned None. This also applies to indirect access through Region.find_element[s] methods.\n- Distributed package now declares explicit dependencies to Selenium and typing_extenstions.\n\n### 1.1.0 (2024-08-07)\nAdd support for python 3.10. \nThe project now officially supports versions 3.8, 3.9, 3.11 and 3.12.\n\n### 1.0.0 (2024-08-06)\nFirst official release.",
"bugtrack_url": null,
"license": null,
"summary": "python page object model for selenium",
"version": "2.0.0",
"project_urls": {
"Homepage": "https://github.com/albinpalsson/pypom-selenium",
"Issues": "https://github.com/albinpalsson/pypom-selenium/issues"
},
"split_keywords": [
"model",
" object",
" page",
" pypom",
" selenium"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "11d07762471a562f3b2b481e9b47c71a9d435bdad2cc68ef256ee2fdc33b3865",
"md5": "f86d498553f6dcddc28a41b8b22d637f",
"sha256": "d7f1879e209787bfd39fc1d430a6949ea987d8c319c525f849c1c06048f5b3eb"
},
"downloads": -1,
"filename": "pypom_selenium-2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f86d498553f6dcddc28a41b8b22d637f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 17271,
"upload_time": "2024-08-15T06:02:24",
"upload_time_iso_8601": "2024-08-15T06:02:24.145832Z",
"url": "https://files.pythonhosted.org/packages/11/d0/7762471a562f3b2b481e9b47c71a9d435bdad2cc68ef256ee2fdc33b3865/pypom_selenium-2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "52bd0cacc7a38b7e596290f66343bc958d08f37030447616e3a599aaa45285a2",
"md5": "ff98df903235d5a9d90731102211499b",
"sha256": "a5607838a7b7f04670f5d2ba850acfc6d13acccc171bb065779449c5c04e14d0"
},
"downloads": -1,
"filename": "pypom_selenium-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "ff98df903235d5a9d90731102211499b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 15580,
"upload_time": "2024-08-15T06:02:25",
"upload_time_iso_8601": "2024-08-15T06:02:25.646025Z",
"url": "https://files.pythonhosted.org/packages/52/bd/0cacc7a38b7e596290f66343bc958d08f37030447616e3a599aaa45285a2/pypom_selenium-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-08-15 06:02:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "albinpalsson",
"github_project": "pypom-selenium",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"tox": true,
"lcname": "pypom-selenium"
}