Skip to content

Commit

Permalink
NEW: predefine _wait_decorator impl in support.*
Browse files Browse the repository at this point in the history
+ bump version to 2.0.0b8
  • Loading branch information
yashaka committed Sep 5, 2022
1 parent f4e6e5a commit 23de88d
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 39 deletions.
49 changes: 45 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,51 @@
- should we make original config (not shared) mutable?
- TODO: support python 3.10 [#393](https://github.com/yashaka/selene/issues/393)

## 2.0.0b8 (to be released on xx.09.2022)
## 2.0.0b9 (to be released on xx.09.2022)
- TODO: trim text in have.exact_text

## 2.0.0b7 (to be released on 02.09.2022)

## 2.0.0b8 (to be released on 05.09.2022)

### NEW: `selene.support._logging.wait_with(context, translations)`

Added selene.support._logging experimental module with «predefined recipe» of wait_decorator for easier logging of Selene waiting commands (yet riskier, cause everything marked as experimental is a subject to change).

Now, given added allure dependency to your project, you can configure logging Selene commands to Allure report as simply as:

```python
from selene.support.shared import browser
from selene import support
import allure_commons

browser.config._wait_decorator = support._logging.wait_with(
context=allure_commons._allure.StepContext
)
```

... or implement your own version of StepContext – feel free to use [Alure's context manager](https://github.com/allure-framework/allure-python/blob/481ea54759b0d8f6aa083a9f70f66cca33cae67c/allure-python-commons/src/_allure.py#L151) as example or the one from Selene's [browser__config__wait_decorator_with_decorator_from_support_logging_test.py](https://github.com/yashaka/selene/tree/master/tests/integration/shared_browser/browser__config__wait_decorator_with_decorator_from_support_logging_test.py) test.

You also can pass a list of translations to be applied to final message to log, something like:

```python
from selene.support.shared import browser
from selene import support
import allure_commons

browser.config._wait_decorator = support._logging.wait_with(
context=allure_commons._allure.StepContext,
translations=(
('browser.element', '$'),
('browser.all', '$$'),
)
)
```

But check the default value for this parameter, maybe you'll be fine with it;)

And remember, the majority of selene extensions from the support.* package, including its `_logging` module – are things you'd better implement on your side to be less dependent to 3rd party helpers;) Feel free to Copy&Paste it into your code and adjust to your needs.

## 2.0.0b7 (released on 02.09.2022)
- BREAKING_CHANGE: change type of config._wait_decorator to access entities, not just commands on them
- from `Callable[[F], F]`
- to `Callable[[Wait[E]], Callable[[F], F]]`
Expand All @@ -99,7 +140,7 @@
- more «frameworkish» one: [examples/log_all_selene_commands_with_wait__framework](https://github.com/yashaka/selene/tree/master/examples/log_all_selene_commands_with_wait__framework)


## 2.0.0b6 (to be released on 31.08.2022)
## 2.0.0b6 (released on 31.08.2022)
- NEW: added "opera" and "edge" support for shared browser
- example:

Expand Down Expand Up @@ -144,7 +185,7 @@
button.click()
```

## 2.0.0b5 (to be released on 24.06.2022)
## 2.0.0b5 (released on 24.06.2022)
- NEW: added command.js.*:
- remove
- set_style_display_to_none
Expand Down
62 changes: 40 additions & 22 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "selene"
version = "2.0.0b7"
version = "2.0.0b8"
description = "User-oriented browser tests in Python (Selenide port)"
authors = ["Iakiv Kramarenko <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -44,6 +44,7 @@ python = "^3.7"
selenium = "==4.2.0"
future = "*"
webdriver-manager = "==3.7.0"
typing-extensions = "==4.3.0"

[tool.poetry.dev-dependencies]
black = "^22.3.0"
Expand Down
2 changes: 1 addition & 1 deletion selene/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
# """
# todo: add here some type imports like Element, Collection, etc.

__version__ = '2.0.0b7'
__version__ = '2.0.0b8'

# --- DEPRECATED, and will be removed soon --- #

Expand Down
2 changes: 1 addition & 1 deletion selene/core/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def as_not(
is_or_have = condition_words[0]
name = ' '.join(condition_words[1:])
no_or_not = 'not' if is_or_have == 'is' else 'no'
new_description = description or f'{is_or_have} {no_or_not} {name}'
new_description = description or f'{is_or_have} {no_or_not} ({name})'

def fn(entity):
try:
Expand Down
4 changes: 3 additions & 1 deletion selene/support/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# todo: consider renaming support to experimental ...
# todo: consider renaming support to _support to emphasize its experimental nature

from . import _logging
82 changes: 82 additions & 0 deletions selene/support/_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from functools import reduce
from typing import Tuple, ContextManager, Dict, Any, Iterable
from typing_extensions import Protocol

from selenium.webdriver import Keys


class _ContextManagerFactory(Protocol):
def __call__(
self, *, title: str, params: Dict[str, Any], **kwargs
) -> ContextManager:
...


class _default_translations:
remove_verbosity = (
('browser.element', 'element'),
('browser.all', 'all'),
("'css selector', ", ""),
('((', '('),
('))', ')'),
)
identify_assertions = (
(': has ', ': have '),
(': have ', ': should have '),
(': is ', ': should be '),
(' and is ', ' and be '),
(' and has ', ' and have '),
)
key_codes_to_names = [
(f"({repr(value)},)", key)
for key, value in Keys.__dict__.items()
if not key.startswith('__')
]


def wait_with(
*,
context: _ContextManagerFactory,
translations: Iterable[Tuple[str, str]] = (
*_default_translations.remove_verbosity,
*_default_translations.identify_assertions,
*_default_translations.key_codes_to_names,
),
):
"""
:return:
Decorator factory to pass to Selene's config._wait_decorator
for logging commands with waiting built in
:param context:
Allure-like ContextManager factory
(i.e. a type/class or function to return python context manager),
that builds a context manager based on title string and params dict
:param translations:
Iterable of translations as (from, to) substitution pairs
to apply to final title string to log
"""

def decorator_factory(wait):
def decorator(for_):
def decorated(fn):

title = f'{wait.entity}: {fn}'

def translate(initial: str, item: Tuple[str, str]):
old, new = item
return initial.replace(old, new)

translated_title = reduce(
translate,
translations,
title,
)

with context(title=translated_title, params={}):
return for_(fn)

return decorated

return decorator

return decorator_factory
12 changes: 12 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import pytest

from selene.support import shared


def pytest_addoption(parser):

parser.addoption(
'--headless',
help='headless mode',
default=False,
)


@pytest.fixture(scope='function')
def quit_shared_browser_afterwards():
yield

shared.browser.quit()
4 changes: 2 additions & 2 deletions tests/integration/condition__collection__have_texts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_should_have_no_texts_exception(session_browser):
with pytest.raises(TimeoutException) as error:
browser.all('li').should(have.no.texts('Alex', 'Yakov'))
# todo: why do we have `has` below, should not it be `have`?
assert "has no texts ('Alex', 'Yakov')" in error.value.msg
assert "has no (texts ('Alex', 'Yakov'))" in error.value.msg
assert "ConditionNotMatchedError: condition not matched" in error.value.msg
# todo: should not we see here actual texts in log too?

Expand Down Expand Up @@ -170,5 +170,5 @@ def test_should_have_no_text_exception(session_browser):

with pytest.raises(TimeoutException) as error:
browser.all('li').should(have.no.text('Alex'))
assert "has no text Alex" in error.value.msg
assert "has no (text Alex)" in error.value.msg
assert "ConditionNotMatchedError: condition not matched" in error.value.msg
2 changes: 1 addition & 1 deletion tests/integration/error_messages_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def test_element_search_fails_with_message_when_explicitly_waits_for_not_conditi

assert exception_message(ex) == [
'Timed out after 0.1s, while waiting for:',
"browser.element(('css selector', '#element')).has no exact text Hello world!",
"browser.element(('css selector', '#element')).has no (exact text Hello world!)",
'',
'Reason: ConditionNotMatchedError: condition not matched',
]
Loading

0 comments on commit 23de88d

Please sign in to comment.