pomcorn 0.3.1__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pomcorn might be problematic. Click here for more details.
- {pomcorn-0.3.1 → pomcorn-0.5.0}/PKG-INFO +3 -7
- {pomcorn-0.3.1 → pomcorn-0.5.0}/README.rst +2 -6
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/component.py +39 -8
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/exceptions.py +4 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/locators/base_locators.py +15 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/page.py +19 -1
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/web_view.py +23 -3
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pyproject.toml +3 -3
- {pomcorn-0.3.1 → pomcorn-0.5.0}/LICENSE +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/__init__.py +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/element.py +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/locators/__init__.py +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/locators/xpath_locators.py +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/py.typed +0 -0
- {pomcorn-0.3.1 → pomcorn-0.5.0}/pomcorn/waits_conditions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pomcorn
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Base implementation of Page Object Model
|
|
5
5
|
Home-page: https://pypi.org/project/pomcorn/
|
|
6
6
|
License: MIT
|
|
@@ -112,8 +112,6 @@ Below is the code that opens ``PyPI.org``, searches for packages by name and pri
|
|
|
112
112
|
packages to the terminal. The script contains all base classes contained in ``pomcorn``: **Page**,
|
|
113
113
|
**ComponentWithBaseLocator**, **ListComponent** and **Element**.
|
|
114
114
|
|
|
115
|
-
.. /* yaspeller ignore:start */
|
|
116
|
-
|
|
117
115
|
.. code-block:: python
|
|
118
116
|
|
|
119
117
|
from typing import Self
|
|
@@ -131,11 +129,11 @@ packages to the terminal. The script contains all base classes contained in ``po
|
|
|
131
129
|
APP_ROOT = "https://pypi.org"
|
|
132
130
|
|
|
133
131
|
def check_page_is_loaded(self) -> bool:
|
|
134
|
-
return self.init_element(
|
|
132
|
+
return self.init_element(locators.TagNameLocator("main")).is_displayed
|
|
135
133
|
|
|
136
134
|
@property
|
|
137
135
|
def search(self) -> Element[locators.XPathLocator]:
|
|
138
|
-
return self.init_element(
|
|
136
|
+
return self.init_element(locators.IdLocator("search"))
|
|
139
137
|
|
|
140
138
|
|
|
141
139
|
# Prepare components
|
|
@@ -185,8 +183,6 @@ packages to the terminal. The script contains all base classes contained in ``po
|
|
|
185
183
|
search_page = SearchPage.open(webdriver=Chrome())
|
|
186
184
|
print(search_page.find("saritasa").names)
|
|
187
185
|
|
|
188
|
-
.. /* yaspeller ignore:end */
|
|
189
|
-
|
|
190
186
|
For more information about package classes, you can read in `Object Hierarchy <https://pomcorn.readthedocs.io/en/latest/objects_hierarchy.html>`_
|
|
191
187
|
and `Developer Interface <https://pomcorn.readthedocs.io/en/latest/developer_interface.html>`_.
|
|
192
188
|
|
|
@@ -86,8 +86,6 @@ Below is the code that opens ``PyPI.org``, searches for packages by name and pri
|
|
|
86
86
|
packages to the terminal. The script contains all base classes contained in ``pomcorn``: **Page**,
|
|
87
87
|
**ComponentWithBaseLocator**, **ListComponent** and **Element**.
|
|
88
88
|
|
|
89
|
-
.. /* yaspeller ignore:start */
|
|
90
|
-
|
|
91
89
|
.. code-block:: python
|
|
92
90
|
|
|
93
91
|
from typing import Self
|
|
@@ -105,11 +103,11 @@ packages to the terminal. The script contains all base classes contained in ``po
|
|
|
105
103
|
APP_ROOT = "https://pypi.org"
|
|
106
104
|
|
|
107
105
|
def check_page_is_loaded(self) -> bool:
|
|
108
|
-
return self.init_element(
|
|
106
|
+
return self.init_element(locators.TagNameLocator("main")).is_displayed
|
|
109
107
|
|
|
110
108
|
@property
|
|
111
109
|
def search(self) -> Element[locators.XPathLocator]:
|
|
112
|
-
return self.init_element(
|
|
110
|
+
return self.init_element(locators.IdLocator("search"))
|
|
113
111
|
|
|
114
112
|
|
|
115
113
|
# Prepare components
|
|
@@ -159,8 +157,6 @@ packages to the terminal. The script contains all base classes contained in ``po
|
|
|
159
157
|
search_page = SearchPage.open(webdriver=Chrome())
|
|
160
158
|
print(search_page.find("saritasa").names)
|
|
161
159
|
|
|
162
|
-
.. /* yaspeller ignore:end */
|
|
163
|
-
|
|
164
160
|
For more information about package classes, you can read in `Object Hierarchy <https://pomcorn.readthedocs.io/en/latest/objects_hierarchy.html>`_
|
|
165
161
|
and `Developer Interface <https://pomcorn.readthedocs.io/en/latest/developer_interface.html>`_.
|
|
166
162
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
from typing import Generic, TypeVar, overload
|
|
3
2
|
|
|
4
3
|
from . import locators
|
|
@@ -34,10 +33,12 @@ class ComponentWithBaseLocator(Generic[TPage], Component[TPage]):
|
|
|
34
33
|
|
|
35
34
|
"""
|
|
36
35
|
|
|
36
|
+
base_locator: locators.XPathLocator
|
|
37
|
+
|
|
37
38
|
def __init__(
|
|
38
39
|
self,
|
|
39
40
|
page: TPage,
|
|
40
|
-
base_locator: locators.XPathLocator,
|
|
41
|
+
base_locator: locators.XPathLocator | None = None,
|
|
41
42
|
wait_until_visible: bool = True,
|
|
42
43
|
):
|
|
43
44
|
"""Initialize component.
|
|
@@ -46,14 +47,14 @@ class ComponentWithBaseLocator(Generic[TPage], Component[TPage]):
|
|
|
46
47
|
page: An instance of the page that uses this component.
|
|
47
48
|
base_locator: locator: Instance of a class to locate the element in
|
|
48
49
|
the browser. Used in relative element initialization methods
|
|
49
|
-
and visibility waits.
|
|
50
|
+
and visibility waits. You also can specify it as attribute.
|
|
50
51
|
wait_until_visible: Whether to wait for the component to become
|
|
51
52
|
visible before completing initialization or not.
|
|
52
53
|
|
|
53
54
|
"""
|
|
54
55
|
super().__init__(page)
|
|
55
|
-
self.base_locator = base_locator
|
|
56
|
-
self.body = self.init_element(locator=base_locator)
|
|
56
|
+
self.base_locator = base_locator or self.base_locator
|
|
57
|
+
self.body = self.init_element(locator=self.base_locator)
|
|
57
58
|
|
|
58
59
|
if wait_until_visible:
|
|
59
60
|
self.wait_until_visible()
|
|
@@ -194,14 +195,44 @@ class ListComponent(
|
|
|
194
195
|
Waits for `base_item_locator` property to be implemented and value of
|
|
195
196
|
`item_class` to be set.
|
|
196
197
|
|
|
198
|
+
Waits for `base_item_locator` property to be overridden or one of the
|
|
199
|
+
attributes (`item_locator` or `relative_item_locator`) to be specified.
|
|
200
|
+
|
|
201
|
+
The `item_class` attribute is also required.
|
|
202
|
+
|
|
197
203
|
"""
|
|
198
204
|
|
|
199
205
|
item_class: type[ListItemType]
|
|
200
206
|
|
|
201
|
-
|
|
207
|
+
item_locator: locators.XPathLocator | None = None
|
|
208
|
+
relative_item_locator: locators.XPathLocator | None = None
|
|
209
|
+
|
|
210
|
+
@property
|
|
202
211
|
def base_item_locator(self) -> locators.XPathLocator:
|
|
203
|
-
"""Get the base locator of list item.
|
|
204
|
-
|
|
212
|
+
"""Get the base locator of list item.
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
ValueError: If both attributes are specified.
|
|
216
|
+
NotImplementedError: If no attribute has been specified,
|
|
217
|
+
|
|
218
|
+
"""
|
|
219
|
+
if self.relative_item_locator and self.item_locator:
|
|
220
|
+
raise ValueError(
|
|
221
|
+
"You only need to specify one of the attributes: "
|
|
222
|
+
"`relative_item_locator` - if you want locator nested within "
|
|
223
|
+
"`base_locator`, `item_locator` - otherwise. "
|
|
224
|
+
"Or override `base_item_locator` property.",
|
|
225
|
+
)
|
|
226
|
+
if not self.relative_item_locator:
|
|
227
|
+
if not self.item_locator:
|
|
228
|
+
raise NotImplementedError(
|
|
229
|
+
"You need to specify one of the arguments: "
|
|
230
|
+
"`relative_item_locator` - if you want locator nested "
|
|
231
|
+
"within `base_locator`, `item_locator` - otherwise. "
|
|
232
|
+
"Or override `base_item_locator` property.",
|
|
233
|
+
)
|
|
234
|
+
return self.item_locator
|
|
235
|
+
return self.base_locator // self.relative_item_locator
|
|
205
236
|
|
|
206
237
|
@property
|
|
207
238
|
def count(self) -> int:
|
|
@@ -127,6 +127,21 @@ class XPathLocator(Locator):
|
|
|
127
127
|
"""Override `//` operator to implement nested XPath locators."""
|
|
128
128
|
return XPathLocator(query=f"{self.query}//{other.related_query}")
|
|
129
129
|
|
|
130
|
+
def __or__(self, other: XPathLocator) -> XPathLocator:
|
|
131
|
+
r"""Override `|` operator to implement variant XPath locators.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
span = XPathLocator("//span")
|
|
135
|
+
div = XPathLocator("//div")
|
|
136
|
+
img = XPathLocator("//img")
|
|
137
|
+
|
|
138
|
+
(span | div) // img == XPathLocator("(//span | //div)//img")
|
|
139
|
+
|
|
140
|
+
span | div // img == XPathLocator("(//span | //div//img)")
|
|
141
|
+
|
|
142
|
+
"""
|
|
143
|
+
return XPathLocator(query=f"({self.query} | {other.query})")
|
|
144
|
+
|
|
130
145
|
def extend_query(self, extra_query: str) -> XPathLocator:
|
|
131
146
|
"""Return new XPathLocator with extended query."""
|
|
132
147
|
return XPathLocator(query=self.query + extra_query)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
from typing import Self
|
|
2
2
|
|
|
3
|
+
from selenium.common.exceptions import TimeoutException
|
|
3
4
|
from selenium.webdriver.remote.webdriver import WebDriver
|
|
4
5
|
|
|
6
|
+
from . import locators
|
|
7
|
+
from .exceptions import PageDidNotLoadedError
|
|
5
8
|
from .web_view import WebView
|
|
6
9
|
|
|
7
10
|
|
|
@@ -116,7 +119,13 @@ class Page(WebView):
|
|
|
116
119
|
|
|
117
120
|
def wait_until_loaded(self) -> None:
|
|
118
121
|
"""Wait until page is loaded."""
|
|
119
|
-
|
|
122
|
+
try:
|
|
123
|
+
self.wait.until(lambda _: self.check_page_is_loaded())
|
|
124
|
+
except TimeoutException:
|
|
125
|
+
raise PageDidNotLoadedError(
|
|
126
|
+
f"Page didn't loaded in {self.wait_timeout} seconds! "
|
|
127
|
+
"Didn't wait for `True` from `check_page_is_loaded` method.",
|
|
128
|
+
)
|
|
120
129
|
|
|
121
130
|
def navigate(self, url: str) -> None:
|
|
122
131
|
"""Navigate absolute URL.
|
|
@@ -137,6 +146,15 @@ class Page(WebView):
|
|
|
137
146
|
self._get_full_relative_url(self.app_root, relative_url),
|
|
138
147
|
)
|
|
139
148
|
|
|
149
|
+
def click_on_page(self):
|
|
150
|
+
"""Click on page `html` tag.
|
|
151
|
+
|
|
152
|
+
Allows you to move focus away from an element, for example, if it
|
|
153
|
+
is currently unavailable for interaction.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
self.init_element(locator=locators.TagNameLocator("html")).click()
|
|
157
|
+
|
|
140
158
|
@staticmethod
|
|
141
159
|
def _get_full_relative_url(app_root: str, relative_url: str) -> str:
|
|
142
160
|
"""Add relative URL to application root URL.
|
|
@@ -47,6 +47,10 @@ class WebView:
|
|
|
47
47
|
def init_element(self, locator: TInitLocator) -> Element[TInitLocator]:
|
|
48
48
|
"""Shortcut for initializing Element instances.
|
|
49
49
|
|
|
50
|
+
Note: To be consistent with the method of the same name in
|
|
51
|
+
``ComponentWithBaseLocator``, try to use keyword when specifying
|
|
52
|
+
the ``locator`` argument whenever possible.
|
|
53
|
+
|
|
50
54
|
Args:
|
|
51
55
|
locator: Instance of a class to locate the element in the browser.
|
|
52
56
|
|
|
@@ -61,6 +65,10 @@ class WebView:
|
|
|
61
65
|
|
|
62
66
|
Note: Only supports Xpath locators.
|
|
63
67
|
|
|
68
|
+
Note: To be consistent with the method of the same name in
|
|
69
|
+
``ComponentWithBaseLocator``, try to use keyword when specifying the
|
|
70
|
+
``locator`` argument whenever possible.
|
|
71
|
+
|
|
64
72
|
Args:
|
|
65
73
|
locator: Instance of a class to locate the element in the browser.
|
|
66
74
|
|
|
@@ -88,9 +96,10 @@ class WebView:
|
|
|
88
96
|
|
|
89
97
|
For example, there are multiple elements on the page that matches
|
|
90
98
|
`XPathLocator("//a")`.
|
|
99
|
+
|
|
91
100
|
This method return the set of locators like:
|
|
92
|
-
* `XPathLocator("//a[1]")`
|
|
93
|
-
* `XPathLocator("//a[2]")`
|
|
101
|
+
* `XPathLocator("(//a)[1]")`
|
|
102
|
+
* `XPathLocator("(//a)[2]")`
|
|
94
103
|
|
|
95
104
|
Args:
|
|
96
105
|
locator: Instance of a class to locate the element in the browser.
|
|
@@ -344,11 +353,22 @@ class WebView:
|
|
|
344
353
|
def scroll_to(self, target: WebElement):
|
|
345
354
|
"""Scroll page to target.
|
|
346
355
|
|
|
356
|
+
Scroll to the center of target vertically and to the center of target
|
|
357
|
+
horizontally.
|
|
358
|
+
|
|
347
359
|
Args:
|
|
348
360
|
target: The web element instance to scroll to.
|
|
349
361
|
|
|
350
362
|
"""
|
|
351
|
-
|
|
363
|
+
# behavior="instant" - to scroll without animation
|
|
364
|
+
# block="center" - vertical scrolling up to center
|
|
365
|
+
# inline="center"- horizontal scrolling up to center
|
|
366
|
+
script = (
|
|
367
|
+
"arguments[0].scrollIntoView("
|
|
368
|
+
"{behavior: 'instant', block: 'center', inline: 'center'}"
|
|
369
|
+
");"
|
|
370
|
+
)
|
|
371
|
+
self.webdriver.execute_script(script, target)
|
|
352
372
|
|
|
353
373
|
def scroll_to_top(self):
|
|
354
374
|
"""Scroll browser to top."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pomcorn"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "Base implementation of Page Object Model"
|
|
5
5
|
authors = [
|
|
6
6
|
"Saritasa <pypi@saritasa.com>",
|
|
@@ -70,13 +70,13 @@ pytest = ">= 7.4.2"
|
|
|
70
70
|
# Flake dependencies are added so that VSCode extension for flake8
|
|
71
71
|
# would work properly
|
|
72
72
|
# https://marketplace.visualstudio.com/items?itemName=ms-python.flake8&ssr=false#overview
|
|
73
|
-
flake8 = "
|
|
73
|
+
flake8 = ">=6.1,<8.0"
|
|
74
74
|
# A flake8 plugin that warn about backslashes usage.
|
|
75
75
|
# https://github.com/wemake-services/flake8-broken-line
|
|
76
76
|
flake8-broken-line = "^1.0.0"
|
|
77
77
|
# A plugin for Flake8 finding likely bugs and design problems in your program.
|
|
78
78
|
# https://github.com/PyCQA/flake8-bugbear
|
|
79
|
-
flake8-bugbear = "
|
|
79
|
+
flake8-bugbear = ">=23.9.16,<25.0.0"
|
|
80
80
|
# A simple module that adds an extension for the fantastic pydocstyle tool to flake8.
|
|
81
81
|
# https://github.com/PyCQA/flake8-docstrings
|
|
82
82
|
flake8-docstrings = "^1.7.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|