Skip to content

Commit

Permalink
[#538] REFACTOR: deprecate *.js_property in favor of query.native_pro…
Browse files Browse the repository at this point in the history
…perty and have.property_

... and match.native_property

since have.* is more high level than match.* or query.*, I decided to keep have.property_ name more concise and less techy...
  • Loading branch information
yashaka committed Jun 23, 2024
1 parent 6b7160a commit 24450d5
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 50 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ especially relevant for have.texts to turn on/off ignoring invisible elements
- `be.not_.present` in favor of `be.not_.present_in_dom`
- `be.absent` in favor of `be.absent_in_dom`
- `be.not_.absent` in favor of `be.not_.absent_in_dom`
- `have.no.js_property` in favor of `have.no.property_`
- `have.js_property` in favor of `have.property_` or `match.native_property`
- same for `query.js_property` in favor of `query.native_property`

### Added be.hidden_in_dom in addition to be.hidden

Expand Down
4 changes: 1 addition & 3 deletions docs/faq/iframes-howto.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ You allways can work with iframes same way [as you do in pure Selenium](https://
```python
from selene import browser, command, have



# GIVEN
iframe = browser.element('#editor-iframe')
# WHEN
Expand All @@ -27,7 +25,7 @@ browser.driver.switch_to.frame(iframe_webelement)
# AND ...
browser.all('strong').should(have.size(1))
browser.element('.textarea').should(
have.js_property('innerHTML').value(
have.property_('innerHTML').value(
'<p><strong>Hello, world!</strong></p>'
)
)
Expand Down
29 changes: 11 additions & 18 deletions selene/core/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,6 @@ def __init__(self, expected: str, _flags=0, _inverted=False):
self.__expected = expected
self.__flags = _flags
self.__inverted = _inverted
# TODO: on invalid pattern error will be:
# 'Reason: ConditionMismatch: nothing to repeat at position 0'
# how to improve it? leaving more hints that this is "regex invalid error"
# probably, we can re-raise re.error inside predicate.matches
# with additional explanation

super().__init__(
f'has text matching{f" (with flags {_flags}):" if _flags else ""}'
Expand All @@ -258,10 +253,12 @@ def __init__(self, expected: str, _flags=0, _inverted=False):
def ignore_case(self):
return self.where_flags(re.IGNORECASE)

# TODO: should we shorten name just to flags? i.e.
# todo: should we shorten name just to flags? (or add alias) i.e.
# `.should(have.text_matching(r'.*one.*').flags(re.IGNORECASE))`
# over
# `.should(have.text_matching(r'.*one.*').where_flags(re.IGNORECASE))`
# currently it's named with where_ prefix for consistency with
# texts_like & co conditions
def where_flags(self, flags: re.RegexFlag, /) -> Condition[Element]:
return self.__class__(
self.__expected,
Expand All @@ -270,32 +267,28 @@ def where_flags(self, flags: re.RegexFlag, /) -> Condition[Element]:
)


def element_has_js_property(name: str):
# TODO: will this even work for mobile? o_O
# if .get_property is valid for mobile
# then we should rename it for sure here...
# TODO: should we keep simpler but less obvious name - *_has_property ?
def native_property(name: str):
def property_value(element: Element):
return element.locate().get_property(name)

def property_values(collection: Collection):
return [element.get_property(name) for element in collection()]

raw_property_condition = ElementCondition.raise_if_not_actual(
'has js property ' + name, property_value, predicate.is_truthy
'has native property ' + name, property_value, predicate.is_truthy
)

class ConditionWithValues(ElementCondition):
class PropertyWithValues(ElementCondition):
def value(self, expected: str | int | float) -> Condition[Element]:
return ElementCondition.raise_if_not_actual(
f"has js property '{name}' with value '{expected}'",
f"has native property '{name}' with value '{expected}'",
property_value,
predicate.str_equals(expected),
)

def value_containing(self, expected: str | int | float) -> Condition[Element]:
return ElementCondition.raise_if_not_actual(
f"has js property '{name}' with value containing '{expected}'",
f"has native property '{name}' with value containing '{expected}'",
property_value,
predicate.str_includes(expected),
)
Expand All @@ -306,7 +299,7 @@ def values(
expected_ = helpers.flatten(expected)

return CollectionCondition.raise_if_not_actual(
f"has js property '{name}' with values '{expected_}'",
f"has native property '{name}' with values '{expected_}'",
property_values,
predicate.str_equals_to_list(expected_),
)
Expand All @@ -317,12 +310,12 @@ def values_containing(
expected_ = helpers.flatten(expected)

return CollectionCondition.raise_if_not_actual(
f"has js property '{name}' with values containing '{expected_}'",
f"has native property '{name}' with values containing '{expected_}'",
property_values,
predicate.str_equals_by_contains_to_list(expected_),
)

return ConditionWithValues(
return PropertyWithValues(
str(raw_property_condition), test=raw_property_condition.__call__
)

Expand Down
12 changes: 10 additions & 2 deletions selene/core/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def fn(element: Element):

import functools
import typing
import warnings

from typing_extensions import (
List,
Expand Down Expand Up @@ -343,13 +344,20 @@ def fn(element: Element) -> str:
return Query(f'css property {name}', fn)


def js_property(
def native_property(
name: str,
) -> Query[Element, Union[str, bool, WebElement, dict]]:
def func(element: Element) -> Union[str, bool, WebElement, dict]:
return element.locate().get_property(name)

return Query(f'js property {name}', func)
return Query(f'native property {name}', func)


def js_property(
name: str,
) -> Query[Element, Union[str, bool, WebElement, dict]]:
warnings.warn('deprecated: use query.native_property instead', DeprecationWarning)
return native_property(name)


# --- Pseudo-queries --- #
Expand Down
16 changes: 12 additions & 4 deletions selene/support/conditions/have.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,24 @@ def text_matching(regex_pattern: str):
return match.text_pattern(regex_pattern)


# TODO: should we use here js.property style (and below for js.returned(...))
# TODO: should we rename it to native_property or simply prop?
# as we did for match.native_property and query.native_property?
def property_(name: str): # named with _ suffix for no conflicts with built in
return match.native_property(name)


def js_property(name: str, value: Optional[str] = None):
warnings.warn(
'have.js_property is deprecated; use have.property instead', DeprecationWarning
)
if value:
warnings.warn(
'passing second argument is deprecated; use have.js_property(foo).value(bar) instead',
'passing second argument is deprecated; use have.property(foo).value(bar) instead',
DeprecationWarning,
)
return match.element_has_js_property(name).value(value)
return match.native_property(name).value(value)

return match.element_has_js_property(name)
return match.native_property(name)


def css_property(name: str, value: Optional[str] = None):
Expand Down
11 changes: 8 additions & 3 deletions selene/support/conditions/not_.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,19 +122,24 @@ def values_containing(self, *expected: str | int | float) -> Condition[Collectio


def js_property(name: str, *args, **kwargs):
warnings.warn('deprecated; use have.no.property instead', DeprecationWarning)
return property_(name, *args, **kwargs)


def property_(name: str, *args, **kwargs):
if args or 'value' in kwargs:
warnings.warn(
'passing second argument is deprecated; '
'use have.js_property(foo).value(bar) instead',
'use have.property(foo).value(bar) instead',
DeprecationWarning,
)
return (
_match.element_has_js_property(name)
_match.native_property(name)
.value(args[0] if args else kwargs['value'])
.not_
)

original = _match.element_has_js_property(name)
original = _match.native_property(name)
negated = original.not_

def value(self, expected: str | int | float) -> Condition[Element]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,21 @@ def test_have_property__condition_variations(session_browser):
exercises.first.type('20')
exercises.second.type('30')

# the following passes too, cause js prop exists, are we ok with that?
# todo: the following passes too, cause js prop exists, are we ok with that?
browser.element('ul').should(have.attribute('id'))

exercises.should(have.js_property('value').values(20, 30))
names.should(have.js_property('value').values_containing(20, 2))
exercises.should(have.property_('value').values(20, 30))
names.should(have.property_('value').values_containing(20, 2))
try:
names.should(have.js_property('value').values_containing(20, 2).not_)
names.should(have.property_('value').values_containing(20, 2).not_)
pytest.fail('should fail on values mismatch')
except AssertionError as error:
assert (
"browser.all(('css selector', '.name')).has no (js property 'value' with "
"browser.all(('css selector', '.name')).has no (property 'value' with "
"values containing '(20, 2)')\n"
'\n'
"Reason: ConditionMismatch: actual property values: ['John 20th', 'Doe 2nd']\n"
) in str(error)

exercises.first.should(have.js_property('value').value(20))
exercises.first.should(have.js_property('value').value_containing(2))
exercises.first.should(have.property_('value').value(20))
exercises.first.should(have.property_('value').value_containing(2))
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def set_bold(self):

@text_area_frame._within
def should_have_text_html(self, text_html):
self.text_area.should(have.js_property('innerHTML').value(text_html))
self.text_area.should(have.property_('innerHTML').value(text_html))
return self

@text_area_frame._within
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_actions_on_frame_element_with_logging(session_browser):

# THEN everything inside frame context
text_area.element('p').should(
have.js_property('innerHTML').value(
have.property_('innerHTML').value(
'Hello, World!',
)
).perform(command.select_all)
Expand All @@ -105,13 +105,13 @@ def test_actions_on_frame_element_with_logging(session_browser):

# AND coming back inside frame context
text_area.element('p').should(
have.js_property('innerHTML').value('<strong>Hello, World!</strong>')
have.property_('innerHTML').value('<strong>Hello, World!</strong>')
)

text_area.perform(command.select_all).type(
'New content',
).element('p').should(
have.js_property('innerHTML').value(
have.property_('innerHTML').value(
'<strong>New content</strong>',
)
)
Expand All @@ -124,19 +124,19 @@ def test_actions_on_frame_element_with_logging(session_browser):
# THEN everything is logged:
assert (
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value 'Hello, World!': STARTED\n"
"have native property 'innerHTML' with value 'Hello, World!': STARTED\n"
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value 'Hello, World!': PASSED\n"
"have native property 'innerHTML' with value 'Hello, World!': PASSED\n"
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): send "
'«select all» keys shortcut as ctrl+a or cmd+a for mac: STARTED\n'
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): send "
'«select all» keys shortcut as ctrl+a or cmd+a for mac: PASSED\n'
"element('.tox-toolbar__primary').element('[aria-label=Bold]'): click: STARTED\n"
"element('.tox-toolbar__primary').element('[aria-label=Bold]'): click: PASSED\n"
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value '<strong>Hello, World!</strong>': STARTED\n"
"have native property 'innerHTML' with value '<strong>Hello, World!</strong>': STARTED\n"
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value '<strong>Hello, World!</strong>': PASSED\n"
"have native property 'innerHTML' with value '<strong>Hello, World!</strong>': PASSED\n"
"element('.tox-edit-area__iframe'): element('#tinymce'): send «select all» "
'keys shortcut as ctrl+a or cmd+a for mac: STARTED\n'
"element('.tox-edit-area__iframe'): element('#tinymce'): send «select all» "
Expand All @@ -146,10 +146,10 @@ def test_actions_on_frame_element_with_logging(session_browser):
"element('.tox-edit-area__iframe'): element('#tinymce'): type: New content: "
'PASSED\n'
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value '<strong>New content</strong>': "
"have native property 'innerHTML' with value '<strong>New content</strong>': "
'STARTED\n'
"element('.tox-edit-area__iframe'): element('#tinymce').element('p'): should "
"have js property 'innerHTML' with value '<strong>New content</strong>': "
"have native property 'innerHTML' with value '<strong>New content</strong>': "
'PASSED\n'
"element('.tox-edit-area__iframe'): element('#tinymce').all('p'): should have "
'size 10: STARTED\n'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_actions_within_frame_context(session_browser):

# THEN
text_area.element('p').should(
have.js_property('innerHTML').value(
have.property_('innerHTML').value(
'Hello, World!',
)
)
Expand All @@ -60,7 +60,7 @@ def test_actions_within_frame_context(session_browser):

# THEN
text_area.element('p').should(
have.js_property('innerHTML').value('<strong>Hello, World!</strong>')
have.property_('innerHTML').value('<strong>Hello, World!</strong>')
)

# WHEN (just one more example)
Expand All @@ -70,7 +70,7 @@ def test_actions_within_frame_context(session_browser):

# THEN
text_area.element('p').should(
have.js_property('innerHTML').value(
have.property_('innerHTML').value(
'<strong>New content</strong>',
)
)

0 comments on commit 24450d5

Please sign in to comment.