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.
Files changed (60) hide show
  1. {promium-3.7.2 → promium-3.7.4}/PKG-INFO +1 -1
  2. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/PKG-INFO +1 -1
  3. {promium-3.7.2 → promium-3.7.4}/promium/_diag.py +26 -4
  4. {promium-3.7.2 → promium-3.7.4}/promium/base.py +5 -5
  5. {promium-3.7.2 → promium-3.7.4}/promium/elements/input_field.py +3 -38
  6. {promium-3.7.2 → promium-3.7.4}/promium/plugin.py +19 -6
  7. {promium-3.7.2 → promium-3.7.4}/setup.py +1 -1
  8. {promium-3.7.2 → promium-3.7.4}/LICENSE +0 -0
  9. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/SOURCES.txt +0 -0
  10. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/dependency_links.txt +0 -0
  11. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/entry_points.txt +0 -0
  12. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/requires.txt +0 -0
  13. {promium-3.7.2 → promium-3.7.4}/Promium.egg-info/top_level.txt +0 -0
  14. {promium-3.7.2 → promium-3.7.4}/README.rst +0 -0
  15. {promium-3.7.2 → promium-3.7.4}/doc/__init__.py +0 -0
  16. {promium-3.7.2 → promium-3.7.4}/doc/conf.py +0 -0
  17. {promium-3.7.2 → promium-3.7.4}/promium/__init__.py +0 -0
  18. {promium-3.7.2 → promium-3.7.4}/promium/assertions.py +0 -0
  19. {promium-3.7.2 → promium-3.7.4}/promium/browsers.py +0 -0
  20. {promium-3.7.2 → promium-3.7.4}/promium/common.py +0 -0
  21. {promium-3.7.2 → promium-3.7.4}/promium/device_config.py +0 -0
  22. {promium-3.7.2 → promium-3.7.4}/promium/elements/__init__.py +0 -0
  23. {promium-3.7.2 → promium-3.7.4}/promium/elements/checkbox.py +0 -0
  24. {promium-3.7.2 → promium-3.7.4}/promium/elements/drop_downs.py +0 -0
  25. {promium-3.7.2 → promium-3.7.4}/promium/elements/frame.py +0 -0
  26. {promium-3.7.2 → promium-3.7.4}/promium/elements/link.py +0 -0
  27. {promium-3.7.2 → promium-3.7.4}/promium/elements/multiselect_listbox.py +0 -0
  28. {promium-3.7.2 → promium-3.7.4}/promium/elements/radio_button.py +0 -0
  29. {promium-3.7.2 → promium-3.7.4}/promium/elements/textarea_field.py +0 -0
  30. {promium-3.7.2 → promium-3.7.4}/promium/elements/upload_button.py +0 -0
  31. {promium-3.7.2 → promium-3.7.4}/promium/exceptions.py +0 -0
  32. {promium-3.7.2 → promium-3.7.4}/promium/expected_conditions.py +0 -0
  33. {promium-3.7.2 → promium-3.7.4}/promium/helpers.py +0 -0
  34. {promium-3.7.2 → promium-3.7.4}/promium/logger.py +0 -0
  35. {promium-3.7.2 → promium-3.7.4}/promium/support/__init__.py +0 -0
  36. {promium-3.7.2 → promium-3.7.4}/promium/support/settings.py +0 -0
  37. {promium-3.7.2 → promium-3.7.4}/promium/test_case.py +0 -0
  38. {promium-3.7.2 → promium-3.7.4}/promium/waits.py +0 -0
  39. {promium-3.7.2 → promium-3.7.4}/pyproject.toml +0 -0
  40. {promium-3.7.2 → promium-3.7.4}/setup.cfg +0 -0
  41. {promium-3.7.2 → promium-3.7.4}/tests/__init__.py +0 -0
  42. {promium-3.7.2 → promium-3.7.4}/tests/config.py +0 -0
  43. {promium-3.7.2 → promium-3.7.4}/tests/conftest.py +0 -0
  44. {promium-3.7.2 → promium-3.7.4}/tests/pages/__init__.py +0 -0
  45. {promium-3.7.2 → promium-3.7.4}/tests/pages/catalog_404_page.py +0 -0
  46. {promium-3.7.2 → promium-3.7.4}/tests/pages/catalog_gallery_page.py +0 -0
  47. {promium-3.7.2 → promium-3.7.4}/tests/settings/__init__.py +0 -0
  48. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_base.py +0 -0
  49. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_chrome.py +0 -0
  50. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_edge.py +0 -0
  51. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_firefox.py +0 -0
  52. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_ie.py +0 -0
  53. {promium-3.7.2 → promium-3.7.4}/tests/settings/test_opera.py +0 -0
  54. {promium-3.7.2 → promium-3.7.4}/tests/test_catalog_404.py +0 -0
  55. {promium-3.7.2 → promium-3.7.4}/tests/test_catalog_gallery.py +0 -0
  56. {promium-3.7.2 → promium-3.7.4}/tests/test_enviroment.py +0 -0
  57. {promium-3.7.2 → promium-3.7.4}/tests/test_requests.py +0 -0
  58. {promium-3.7.2 → promium-3.7.4}/tests/test_soft_asserts.py +0 -0
  59. {promium-3.7.2 → promium-3.7.4}/tests/test_waits.py +0 -0
  60. {promium-3.7.2 → promium-3.7.4}/tests/urls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Promium
3
- Version: 3.7.2
3
+ Version: 3.7.4
4
4
  Summary: Selenium wrapper for testing Web UI
5
5
  Author: Denis Korytkin, Nataliia Guieva, Roman Zaporozhets, Vladimir Kritov, Oleh Dykusha
6
6
  Project-URL: Home page, https://none
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Promium
3
- Version: 3.7.2
3
+ Version: 3.7.4
4
4
  Summary: Selenium wrapper for testing Web UI
5
5
  Author: Denis Korytkin, Nataliia Guieva, Roman Zaporozhets, Vladimir Kritov, Oleh Dykusha
6
6
  Project-URL: Home page, https://none
@@ -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", "6"))
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
- return " → ".join(parts)
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" + "\n\n".join(blocks)
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, time: int = 10, msg: str = "") -> bool | None:
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
- time: Maximum wait time in seconds.
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}", {time}s)')
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, time).until(
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=time,
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 only WebElement/ActionChains to avoid
53
- overridden send_keys."""
54
- with contextlib.suppress(Exception):
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 os.getenv("PROMIUM_ASSERTION_PANEL_MONO_COLOR", False): # noqa: PLW1508
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 os.getenv("PROMIUM_ASSERTION_PANEL_MONO_COLOR", False): # noqa: PLW1508
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
- Text(error_text.strip(), justify="left"),
321
+ renderable=msg,
314
322
  title=color_msg("Soft Assertion Error"),
315
323
  border_style=border_style,
316
- padding=(1, 2),
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
- Text(error_text.strip(), justify="left"),
353
+ renderable=msg,
341
354
  title=color_msg("Browser console js errors found"),
342
355
  border_style=border_style,
343
- padding=(1, 2),
356
+ padding=(1, 1),
344
357
  )
345
358
  console.print(rich_panel)
346
359
  rich_output = string_io.getvalue()
@@ -19,7 +19,7 @@ LONG_DESCRIPTION = Path("README.rst").read_text(encoding="utf-8")
19
19
 
20
20
  setup(
21
21
  name="Promium",
22
- version="3.7.2",
22
+ version="3.7.4",
23
23
  install_requires=REQUIREMENTS,
24
24
  author=(
25
25
  "Denis Korytkin, Nataliia Guieva, "
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