Promium 3.7.2__tar.gz → 3.7.4__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.
- {promium-3.7.2 → promium-3.7.4}/PKG-INFO +1 -1
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/PKG-INFO +1 -1
- {promium-3.7.2 → promium-3.7.4}/promium/_diag.py +26 -4
- {promium-3.7.2 → promium-3.7.4}/promium/base.py +5 -5
- {promium-3.7.2 → promium-3.7.4}/promium/elements/input_field.py +3 -38
- {promium-3.7.2 → promium-3.7.4}/promium/plugin.py +19 -6
- {promium-3.7.2 → promium-3.7.4}/setup.py +1 -1
- {promium-3.7.2 → promium-3.7.4}/LICENSE +0 -0
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/SOURCES.txt +0 -0
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/dependency_links.txt +0 -0
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/entry_points.txt +0 -0
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/requires.txt +0 -0
- {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/top_level.txt +0 -0
- {promium-3.7.2 → promium-3.7.4}/README.rst +0 -0
- {promium-3.7.2 → promium-3.7.4}/doc/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/doc/conf.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/assertions.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/browsers.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/common.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/device_config.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/checkbox.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/drop_downs.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/frame.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/link.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/multiselect_listbox.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/radio_button.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/textarea_field.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/elements/upload_button.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/exceptions.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/expected_conditions.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/helpers.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/logger.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/support/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/support/settings.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/test_case.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/promium/waits.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/pyproject.toml +0 -0
- {promium-3.7.2 → promium-3.7.4}/setup.cfg +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/config.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/conftest.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/pages/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/pages/catalog_404_page.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/pages/catalog_gallery_page.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/__init__.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_base.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_chrome.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_edge.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_firefox.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_ie.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/settings/test_opera.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_catalog_404.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_catalog_gallery.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_enviroment.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_requests.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_soft_asserts.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/test_waits.py +0 -0
- {promium-3.7.2 → promium-3.7.4}/tests/urls.py +0 -0
|
@@ -12,8 +12,17 @@ from typing import Any
|
|
|
12
12
|
STEP_BUFFER = int(os.getenv("PROMIUM_STEP_BUFFER", "50"))
|
|
13
13
|
|
|
14
14
|
LOCATORS_TO_SHOW = int(
|
|
15
|
-
os.getenv("PROMIUM_LOCATORS_TO_SHOW", os.getenv("LOC_CHAIN_LEN", "
|
|
15
|
+
os.getenv("PROMIUM_LOCATORS_TO_SHOW", os.getenv("LOC_CHAIN_LEN", "3"))
|
|
16
16
|
)
|
|
17
|
+
# PROMIUM_ACTIONS_STYLE controls how the “Actions” chain is rendered:
|
|
18
|
+
# - "stair" (default): multi-line staircase with indentation
|
|
19
|
+
# - "line" : single-line with arrows
|
|
20
|
+
ACTIONS_STYLE_DEFAULT = os.getenv("PROMIUM_ACTIONS_STYLE", "stair").lower()
|
|
21
|
+
ACTIONS_STAIR_INDENT = int(os.getenv("PROMIUM_ACTIONS_STAIR_INDENT", "4"))
|
|
22
|
+
ACTIONS_TOTAL_LIMIT = int(
|
|
23
|
+
os.getenv("PROMIUM_ACTIONS_TOTAL_LIMIT", str(LOCATORS_TO_SHOW))
|
|
24
|
+
)
|
|
25
|
+
|
|
17
26
|
|
|
18
27
|
STEPS_EXCLUDE = [
|
|
19
28
|
s
|
|
@@ -228,7 +237,20 @@ def _format_actions_chain(
|
|
|
228
237
|
parts = _dedup_consecutive(parts)
|
|
229
238
|
if not parts:
|
|
230
239
|
return None
|
|
231
|
-
|
|
240
|
+
|
|
241
|
+
total_limit = max(1, int(ACTIONS_TOTAL_LIMIT or LOCATORS_TO_SHOW))
|
|
242
|
+
if len(parts) > total_limit:
|
|
243
|
+
parts = parts[-total_limit:]
|
|
244
|
+
|
|
245
|
+
style = ACTIONS_STYLE_DEFAULT
|
|
246
|
+
if style != "stair" or len(parts) == 1:
|
|
247
|
+
return " \u2192 ".join(parts)
|
|
248
|
+
|
|
249
|
+
indent_step = max(0, int(ACTIONS_STAIR_INDENT))
|
|
250
|
+
lines: list[str] = [parts[0]]
|
|
251
|
+
for i, p in enumerate(parts[1:], start=1):
|
|
252
|
+
lines.append(f"{' ' * (indent_step * i)}\u2192 {p}")
|
|
253
|
+
return "\n" + "\n".join(lines)
|
|
232
254
|
|
|
233
255
|
|
|
234
256
|
def _read_source_line(path: str, lineno: int, max_len: int) -> str | None:
|
|
@@ -283,7 +305,7 @@ def build_digest(
|
|
|
283
305
|
|
|
284
306
|
actions = _format_actions_chain(context_prefix)
|
|
285
307
|
if actions:
|
|
286
|
-
blocks.append("— Actions: " + actions)
|
|
308
|
+
blocks.append("— Actions: \n" + actions)
|
|
287
309
|
|
|
288
310
|
if SHOW_CALLSITE:
|
|
289
311
|
cs = _user_callsite()
|
|
@@ -300,4 +322,4 @@ def build_digest(
|
|
|
300
322
|
if origin and origin[0]:
|
|
301
323
|
blocks.append(f"↳ locator at {_rel(origin[0])}:{origin[1] or '?'}")
|
|
302
324
|
|
|
303
|
-
return "\n
|
|
325
|
+
return "\n\n".join(blocks)
|
|
@@ -940,12 +940,12 @@ class ElementBase(Base):
|
|
|
940
940
|
digest = f"{digest}\n{msg}"
|
|
941
941
|
raise ElementLocationException(digest) from None
|
|
942
942
|
|
|
943
|
-
def wait_to_disappear(self,
|
|
943
|
+
def wait_to_disappear(self, timeout: int = 10, msg: str = "") -> bool | None:
|
|
944
944
|
"""
|
|
945
945
|
Wait until element disappears from DOM or is hidden.
|
|
946
946
|
|
|
947
947
|
Args:
|
|
948
|
-
|
|
948
|
+
timeout: Maximum wait time in seconds.
|
|
949
949
|
msg: Optional extra message for error context.
|
|
950
950
|
|
|
951
951
|
Raises:
|
|
@@ -956,7 +956,7 @@ class ElementBase(Base):
|
|
|
956
956
|
"""
|
|
957
957
|
css = self._fmt_locator()
|
|
958
958
|
css_probe = self.locator if self.by == By.CSS_SELECTOR else None
|
|
959
|
-
push_step(f'wait_to_disappear("{css}", {
|
|
959
|
+
push_step(f'wait_to_disappear("{css}", {timeout}s)')
|
|
960
960
|
|
|
961
961
|
def _gone(_self: Self) -> bool:
|
|
962
962
|
obj = _self._get_current_driver()
|
|
@@ -972,7 +972,7 @@ class ElementBase(Base):
|
|
|
972
972
|
return True
|
|
973
973
|
|
|
974
974
|
try:
|
|
975
|
-
return WebDriverWait(self.driver,
|
|
975
|
+
return WebDriverWait(self.driver, timeout).until(
|
|
976
976
|
lambda driver: _gone(self)
|
|
977
977
|
)
|
|
978
978
|
except TimeoutException:
|
|
@@ -980,7 +980,7 @@ class ElementBase(Base):
|
|
|
980
980
|
self.driver,
|
|
981
981
|
title="Element(s) did not disappear",
|
|
982
982
|
locator=css,
|
|
983
|
-
wait_seconds=
|
|
983
|
+
wait_seconds=timeout,
|
|
984
984
|
css_for_probe=css_probe,
|
|
985
985
|
**self._digest_ctx(),
|
|
986
986
|
)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
|
|
3
|
-
from selenium.webdriver.common.action_chains import ActionChains
|
|
4
3
|
from selenium.webdriver.common.keys import Keys
|
|
5
4
|
|
|
6
5
|
from promium.base import Element
|
|
@@ -49,43 +48,9 @@ class InputField(Element):
|
|
|
49
48
|
self.lookup().send_keys(*keys)
|
|
50
49
|
|
|
51
50
|
def _clear_field_with_keyboard(self) -> None:
|
|
52
|
-
"""Clear the field using
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
push_step(f'clear_with_keyboard("{self._fmt_locator()}")')
|
|
56
|
-
el = self.lookup()
|
|
57
|
-
with contextlib.suppress(Exception):
|
|
58
|
-
el.click()
|
|
59
|
-
for mod in (Keys.CONTROL, Keys.COMMAND):
|
|
60
|
-
try:
|
|
61
|
-
el.send_keys(mod, "a")
|
|
62
|
-
break
|
|
63
|
-
except TypeError:
|
|
64
|
-
try:
|
|
65
|
-
ActionChains(self.driver).key_down(mod).send_keys(
|
|
66
|
-
"a"
|
|
67
|
-
).key_up(mod).perform()
|
|
68
|
-
break
|
|
69
|
-
except Exception:
|
|
70
|
-
pass
|
|
71
|
-
except Exception:
|
|
72
|
-
continue
|
|
73
|
-
cleared = False
|
|
74
|
-
for key in (Keys.DELETE, Keys.BACKSPACE):
|
|
75
|
-
try:
|
|
76
|
-
el.send_keys(key)
|
|
77
|
-
cleared = True
|
|
78
|
-
break
|
|
79
|
-
except TypeError:
|
|
80
|
-
with contextlib.suppress(Exception):
|
|
81
|
-
ActionChains(self.driver).send_keys(key).perform()
|
|
82
|
-
cleared = True
|
|
83
|
-
break
|
|
84
|
-
except Exception:
|
|
85
|
-
continue
|
|
86
|
-
if not cleared:
|
|
87
|
-
with contextlib.suppress(Exception):
|
|
88
|
-
el.clear()
|
|
51
|
+
"""Clear the input field using keyboard shortcuts (Ctrl+A, Backspace)"""
|
|
52
|
+
self.send_keys(f"{Keys.CONTROL}a")
|
|
53
|
+
self.send_keys(Keys.BACKSPACE)
|
|
89
54
|
|
|
90
55
|
def clear_and_fill(self, text: str) -> str | None:
|
|
91
56
|
"""Clear the field using keyboard and fill it with the given text."""
|
|
@@ -19,6 +19,8 @@ from promium.logger import Logger
|
|
|
19
19
|
from promium.test_case import create_driver
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
mono_color = os.getenv("PROMIUM_ASSERTION_PANEL_MONO_COLOR", False) # noqa: PLW1508
|
|
23
|
+
|
|
22
24
|
log = logging.getLogger(__name__)
|
|
23
25
|
|
|
24
26
|
|
|
@@ -270,7 +272,7 @@ def logger(request: pytest.FixtureRequest) -> logging.Logger:
|
|
|
270
272
|
|
|
271
273
|
|
|
272
274
|
def color_msg(msg: str, color: str = "bold red") -> str:
|
|
273
|
-
if
|
|
275
|
+
if mono_color:
|
|
274
276
|
return msg
|
|
275
277
|
return f"[{color}]{msg}[/{color}]"
|
|
276
278
|
|
|
@@ -281,6 +283,7 @@ def pytest_runtest_call(item: pytest.Item) -> None:
|
|
|
281
283
|
if PANEL_WIDTH := os.getenv("PROMIUM_ASSERTION_PANEL_WIDTH"):
|
|
282
284
|
from rich.console import Console # noqa: PLC0415
|
|
283
285
|
from rich.panel import Panel # noqa: PLC0415
|
|
286
|
+
from rich.syntax import Syntax # noqa: PLC0415
|
|
284
287
|
from rich.text import Text # noqa: PLC0415
|
|
285
288
|
|
|
286
289
|
try:
|
|
@@ -294,7 +297,7 @@ def pytest_runtest_call(item: pytest.Item) -> None:
|
|
|
294
297
|
PANEL_WIDTH = terminal_width
|
|
295
298
|
except Exception:
|
|
296
299
|
PANEL_WIDTH = terminal_width
|
|
297
|
-
if
|
|
300
|
+
if mono_color:
|
|
298
301
|
border_style = "none"
|
|
299
302
|
else:
|
|
300
303
|
border_style = "red"
|
|
@@ -309,11 +312,16 @@ def pytest_runtest_call(item: pytest.Item) -> None:
|
|
|
309
312
|
width=PANEL_WIDTH - 5,
|
|
310
313
|
)
|
|
311
314
|
for error_text in item.config.assertion_errors:
|
|
315
|
+
text = error_text.strip()
|
|
316
|
+
if mono_color:
|
|
317
|
+
msg = Text(text, justify="left")
|
|
318
|
+
else:
|
|
319
|
+
msg = Syntax(text, "sql", word_wrap=True)
|
|
312
320
|
rich_panel = Panel(
|
|
313
|
-
|
|
321
|
+
renderable=msg,
|
|
314
322
|
title=color_msg("Soft Assertion Error"),
|
|
315
323
|
border_style=border_style,
|
|
316
|
-
padding=(1,
|
|
324
|
+
padding=(1, 1),
|
|
317
325
|
)
|
|
318
326
|
console.print(rich_panel)
|
|
319
327
|
rich_output = string_io.getvalue()
|
|
@@ -336,11 +344,16 @@ def pytest_runtest_call(item: pytest.Item) -> None:
|
|
|
336
344
|
width=PANEL_WIDTH - 5,
|
|
337
345
|
)
|
|
338
346
|
for error_text in console_errors:
|
|
347
|
+
text = error_text.strip()
|
|
348
|
+
if mono_color:
|
|
349
|
+
msg = Text(text, justify="left")
|
|
350
|
+
else:
|
|
351
|
+
msg = Syntax(text, "sql", word_wrap=True)
|
|
339
352
|
rich_panel = Panel(
|
|
340
|
-
|
|
353
|
+
renderable=msg,
|
|
341
354
|
title=color_msg("Browser console js errors found"),
|
|
342
355
|
border_style=border_style,
|
|
343
|
-
padding=(1,
|
|
356
|
+
padding=(1, 1),
|
|
344
357
|
)
|
|
345
358
|
console.print(rich_panel)
|
|
346
359
|
rich_output = string_io.getvalue()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|