Selene

Latest version: v1.0.2

Safety actively analyzes 622163 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 16

3.0

For example, `config.browser_name` is deprecated in favor of `config.driver_name`. Main reason – «browser» term is not relevant to mobile testing, where in a lot of cases we test user actions in app, not browser.

New

`from selene import browser`

– to be used instead of `from selene.support.shared import browser`.

No difference between Config and SharedConfig anymore. The new, completely refactored, Config is now used everywhere and allows to customize browser instance in a more convenient way.

Adds ability to use `browser.with_(**config_options_to_override)` to create new browser instance, for example:

python
from selene import browser

chrome = browser
firefox = browser.with_(driver_name='firefox')
edge = browser.with_(driver_name='edge')
...
customizing all browsers at once:
browser.config.timeout = 10


as alternative to:

python
from selene import Browser, Config

chrome = Browser(Config())
firefox = Browser(Config(driver_name='firefox'))
edge = Browser(Config(driver_name='edge'))

...

customizing all browsers:
chrome.config.timeout = 10
firefox.config.timeout = 10
edge.config.timeout = 10


`browser.config.driver_options` + `browser.config.driver_remote_url`

Finally, you can delegate building driver to config manager by passing `driver_options` and `driver_remote_url` to it:

python
import dotenv
from selenium import webdriver
from selene import browser, have


def test_complete_task():
options = webdriver.ChromeOptions()
options.browser_version = '100.0'
options.set_capability(
'selenoid:options',
{
'screenResolution': '1920x1080x24',
'enableVNC': True,
'enableVideo': True,
'enableLog': True,
},
)
browser.config.driver_options = options <- 🥳
project_config = dotenv.dotenv_values()
browser.config.driver_remote_url = ( <- 🎉🎉🎉
f'https://{project_config["LOGIN"]}:{project_config["PASSWORD"]}'
f'selenoid.autotests.cloud/wd/hub'
)

browser.open('http://todomvc.com/examples/emberjs/')
browser.should(have.title_containing('TodoMVC'))

browser.element('new-todo').type('a').press_enter()
browser.element('new-todo').type('b').press_enter()
browser.element('new-todo').type('c').press_enter()
browser.all('todo-list>li').should(have.exact_texts('a', 'b', 'c'))


`browser.open()` without args

Will just open driver or do nothing if driver is already opened.

Can also load page from `browser.config.base_url` if it is set and additional experimental `browser.config._get_base_url_on_open_with_no_args = True` option is set (that is `False` by default).

Automatic driver rebuilding still happens on `browser.open`, but...

but can be configured as follows:

- can be disabled by setting `browser.config.__reset_not_alive_driver_on_get_url = False`,
that is `True` by default
- can be enabled on any explicit or implicit call to `browser.config.driver`,
if set `browser.config.rebuild_not_alive_driver = True` (that is `False` by default)

Appium support out of the box:)

Yet you have to install it manually. But given installed via `pip install Appium-Python-Client` or something like `poetry add Appium-Python-Client`, running tests on mobile devices is as easy as...

Running locally against Appium server:

python
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selene import browser, have

android_options = UiAutomator2Options()
android_options.new_command_timeout = 60
android_options.app = 'wikipedia-alpha-universal-release.apk'
android_options.app_wait_activity = 'org.wikipedia.*'
browser.config.driver_options = android_options
Possible, but not needed, because will be used by default:
browser.config.driver_remote_url = 'http://127.0.0.1:4723/wd/hub'

by_id = lambda id: (AppiumBy.ID, f'org.wikipedia.alpha:id/{id}')

GIVEN
browser.open()
browser.element(by_id('fragment_onboarding_skip_button')).click()

WHEN
browser.element((AppiumBy.ACCESSIBILITY_ID, 'Search Wikipedia')).click()
browser.element(by_id('search_src_text')).type('Appium')

THEN
browser.all(by_id('page_list_item_title')).should(
have.size_greater_than(0)
)


Running remotely against Browserstack server:

python
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selene import browser, have

options = UiAutomator2Options()
options.app = 'bs://c700ce60cf13ae8ed97705a55b8e022f13c5827c'
options.set_capability(
'bstack:options',
{
'deviceName': 'Google Pixel 7',
'userName': 'adminadminovych_qzqzqz',
'accessKey': 'qzqzqzqzqzqzqzqzqzqz',
},
)
browser.config.driver_options = options
browser.config.driver_remote_url = 'http://hub.browserstack.com/wd/hub'

by_id = lambda id: (AppiumBy.ID, f'org.wikipedia.alpha:id/{id}')

GIVEN
browser.open() not needed, but to explicitly force appium to open app

WHEN
browser.element((AppiumBy.ACCESSIBILITY_ID, 'Search Wikipedia')).click()
browser.element(by_id('search_src_text')).type('Appium')

THEN
browser.all(by_id('page_list_item_title')).should(
have.size_greater_than(0)
)



A lot of other local, remote and mobile test examples at...

https://github.com/yashaka/selene/tree/master/examples

autocomplete for entity.with_(HERE)

Other

Deprecated

- `browser.save_screenshot` in favor of `browser.get(query.screenshot_saved())`
- `browser.save_page_source` in favor of `browser.get(query.page_source_saved())`
- `browser.last_screenshot` in favor of `browser.config.last_screenshot`
- `browser.last_page_source` in favor of `browser.config.last_page_source`
- `match.browser_has_js_returned` in favor of `match.browser_has_script_returned`
- `have.js_returned` in favor of `have.script_returned`
- `have.js_returned_true(...)` in favor of `have.script_returned(True, ...)`
- `browser.config.get_or_create_driver`
- `browser.config.reset_driver`
- use `selene.browser.config.driver = ...`
- `browser.config.browser_name` in favor of `browser.config.driver_name`

Removed

- from selene.support.shared import SharedConfig, SharedBrowser
- from selene.core.entity import BrowserCondition, ElementCondition, CollectionCondition

Removed deprecated
- shared.browser.config.desired_capabilities
- shared.browser.config.start_maximized
- shared.browser.config.start_maximized
- shared.browser.config.cash_elements
- shared.browser.config.quit_driver
- shared.browser.latest_page_source
- shared.browser.quit_driver
- shared.browser.set_driver
- shared.browser.open_url
- shared.browser.elements
- shared.browser.wait_to
- shared.browser.title
- shared.browser.take_screenshot
- jquery_style_selectors

Removed not deprecated

- shared.browser.config.Source
- renamed to shared.browser.config._Source.
Currently, is used nowhere in Selene
- shared.browser.config.set_driver (getter and setter)
- shared.browser.config.counter
- use shared.browser.config._counter instead, and better – not use it;)
- shared.browser.config.generate_filename
- use shared.browser.config._generate_filename instead, and better – not use it;)

2.0.0b15-b17

Dependencies
- update selenium (with weakened dependency to >=4.4.3)
- update webdriver_manager (with weakened dependency to >=3.8.5)

2.0.0

TODOs:

- manager or executor at `config._executor`?
- deprecate config.wait(entity) factory?
- isn't it enough to have `config._build_wait_strategy(entity)`?
- decide on `config._reset_not_alive_driver_on_get_url` name
- reset vs rebuild?
- etc?
- decide on None as default in managed driver descriptor instead of ...
taking into account that mypy does not like it
- actually I tend to keep both `...` and `None`, yet using `...` as default
in Selene's internals. But let's document this at least.
Mentioning also this: https://github.com/python/mypy/issues/7818
- consider a way to customize "locator description" [439](https://github.com/yashaka/selene/issues/439)
- consider storing "raw selector" of locator to be able to log it as it is [438](https://github.com/yashaka/selene/issues/438)
- consider storing `locator.name` and take it from class attribute name if Element object
is used as descriptor (via `__set_name__` impl.)
- while being a descriptor...
check if `hassattr(owner, 'element')` (or context, or container?)
then consider starting the search from the context
and make this configurable via `config.search_from_defined_context = True`
(probably `False` by default)
and being able to override somehow on element definition level
- what about `config.search_context_strategy` ?
- ensure we can't element.type on invisible element; add test for that
- decide on have.size vs query.size
- consider making have.size to work with elements too...
- review all ` type: ignore`
- review all typing.cast
- fix «too much screenshots»? if can reproduce
- what about accepting None as locator of Element?
in such case it such element will just do nothing regardless of what command is called on it
- even better, we can accept Locators in browser.element(here)!!!
and so we can implement own behavior, locating some kind of proxy that skips all commands!
- deprecate `be.present`
- todo consider adding element.caching as lazy version of element.cached
- use `__all__` in selene api imports, etc
- The variable `__all__` is a list of public objects of that module, as interpreted by `import *`. ... In other words, `__all__` is a list of strings defining what symbols in a module will be exported when `from module import *` is used on the module
- config.driver_proxy or config.driver_remote_proxy?
- decide on driver as callable, and decide on driver as callable with config as param
- how can we check that user passed fn with params?
- consider renaming everything inside match.* so it can be readable when used as `.should(match.*)`
- take into account that `.matching(match.*)` is yet less readable
compared to `matching(have.*)` or `matching(be.*)`

2.0.0rc9

Click with offsets

As simple as that:

python
from selene import browser, command

...

browser.element('point1').click(xoffset=-5, yoffset=5) relative from center
browser.element('point1').click() still works as before (clicking at center)
with js too:
browser.element('point1').perform(command.js.click(xoffset=-5, yoffset=5))
browser.element('point1').perform(command.js.click()) also works
browser.element('point1').perform(command.js.click) still works as before
or:
browser.element('point1').with_(click_by_js=True).click(xoffset=-5, yoffset=5)


Smarter command.select_all

Seems like the `send_keys(Keys.COMMAND + 'a' + Keys.NULL)` receipe has stopped working since some Selenium version...
So we update the command.select_all implementation to be based on ActionChains, and also work both on browser and element. Here go two examples that demonstrate the new behavior:

when called on element:

pytnon
page.opened_with_body('<input id="text-field" value="text"></input>')

browser.element('text-field').perform(command.select_all).type('reset')

browser.element('text-field').should(have.value('reset'))


when called on browser:

pytnon
page.opened_with_body('<input id="text-field" value="text"></input>')

browser.element('text-field').click() <- MANDATORY to make the input focused

browser.perform(command.select_all)
browser.element('text-field').type('reset')

browser.element('text-field').should(have.value('reset'))


__qualname__ support in context of rendering conditions in error messages

Allows to simplify custom conditions implementation to something like:

python
class have:
staticmethod
def attribute(entity):
if entity.attribute is None:
raise AssertionError('attribute is None')


Since the `have.attribute` staticmethod will already have `__qualname__` defined and equal to `'have.attribute'`, that will result in same rendering of the condition name in error messages on failed waiting (`entity.wait.for_(condition)`) or assertion (via `entity.should(condition)`).

2.0.0rc8

Nicer logging of "reason" in error messages

– by removed stacktrace in processing of timeout exception at wait.py (thanks to [jacekziembla](https://github.com/jacekziembla))

2.0.0rc7

Experimental browser._actions

`browser._actions` is an instance of experimental _Actions class – an alternative implementation of ActionChains from Selenium...

So you can use:

python
from selene import browser
from selene.support.shared.jquery_style import s

browser._actions.move_to(s('point1')).pause(1).click_and_hold(s('point1')).pause(1).move_by_offset(0, 5).move_to(s('point2')).pause(1).release().perform()


instead of something like:

python
from selene import browser
from selene.support.shared.jquery_style import s
from selenium.webdriver.common.action_chains import ActionChains

ActionChains(browser.driver).move_to_element(s('point1').locate()).pause(1).click_and_hold(s('point1').locate()).pause(1).move_by_offset(0, 5).move_to_element(s('point2').locate()).pause(1).release().perform()


or actually even instead of this:

python
from selene import browser, be
from selene.support.shared.jquery_style import s
from selenium.webdriver.common.action_chains import ActionChains

ActionChains(browser.driver).move_to_element(s('point1').should(be.in_dom).locate()).pause(1).click_and_hold(s('point1').should(be.in_dom).locate()).pause(1).move_by_offset(0, 5).move_to_element(s('point2').should(be.in_dom).locate()).pause(1).release().perform()


Here are advantages of Selene's _actions over Selenium's ActionChains:

* the code is more concise
* you can pass Selene's elements to it, instead of Selenium's webelements
* adding new command to the chain automatically includes automatic waiting for element to be in DOM
* if some error happens inside `.perform` – it will be automatically retried in context of common Selene's implicit waiting logic

Here are some open points regarding this implementation and why this feature is marked as experimental:
* the implicit waiting are yet not same powerful as in other Selene's commands
* error messages are less readable, too low level
* not sure if retry logic inside `.perform` is needed at all... can hardly imagine any failure there that can be fixed by retrying
* not sure how will it work with Appium drivers...

Some inner refactoring...

* moved Browser class from selene.core.entity.py to selene.core._browser.py
(yet the module is named as experimental, yet the safest way to import Browser is `from selene import Browser` that is unchanged!)

2.0.0rc6

Goodbye python 3.7 and webdriver-manager 👋🏻

* drop py3.7 support + upgrade selenium>=4.12.0
* drop webdriver-manager in favor of Selenium Manager

Page 1 of 16

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.