Metadata-Version: 2.1
Name: selenium-actions
Version: 1.0.3
Summary: Selenium Actions - Action Based Selenium library - selenium in more accessible way :)
Author-email: Tomasz Majk <tmajk@saskodzi.pl>
License: MIT License
        
        Copyright (c) 2020 Tomasz Majk
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/bigSAS/selenium-actions
Keywords: selenium,actions,selenium-actions,seleniumactions,saskodzi,omni-selenium
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.6
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE

# Selenium Actions

PyPI - https://pypi.org/project/selenium-actions/

todo: summary

## Real life examples

* behave - https://github.com/bigSAS/selenium-actions-behave-example
* pytest - todo

## Actions
### Create actions object
todo
### Examples

Example access to actions (behave):

```python
from behave import step
from seleniumactions import Actions


@step('some cool gherkin step')
def mystep(context):  # actions has been instatnciated by fixture in behave and attached to context
    actions: Actions = context.actions
```

From now on you can use actions everywhere 🚀

### Examples

```python
from seleniumactions import Actions, LocatorExists


actions: Actions = context.actions

# locators (tuples)
main_header = ('xpath', '//h1[.="Home"]')
menu = ('id', 'menu')
news_option = ('xpath', '//menu-option[.="News"]')
search_input = ('xpath', '//search-news//input')
form = ('xpath', '//form')

# take some actions 🚀
actions.goto('https://some.site.io')  # open site
actions.wait_for(LocatorExists(main_header))  # wait for condition to be met with default timeout from configuration applied
actions.click(menu)  # default timeout from configuration applied
actions.click(news_option, timeout='medium')  # 'medium' timeout from configuration applied
actions.type_text(search_input, text='python', explicit_timeout=3)  # explicit timeout in seconds (always overrides any timeout from configuration)
actions.submit()  # submit default form (//form) - works when theres only one form on page
# actions.submit(form)  # u can pass form locator also
actions.wait_for(LocatorExists(('xpath', '//search-results')), timeout='long')  # wait for condition with 'long' timeout from configuration applied

# assert ect...
```

## Locators

Lest say we have HTML component (simplified for example 👀)


```html
<ul>
    <li class="menu"> Foo</li>
    <li class="menu"> Bar </li>
    <li class="menu">Baz </li>
</ul>
```

We want to  be able to click each one of them. So the xpath values for them will be:

```python
foo_xpath = "//ul/li[@class='menu' and contains(., 'Foo')]"
bar_xpath = "//ul/li[@class='menu' and contains(., 'Bar')]"
baz_xpath = "//ul/li[@class='menu' and contains(., 'Baz')]"
```

It can be painfull. Of course you can write a function and parametrize the string

```python
def get_menu_xpath(label: str):
    return f"//ul/li[@class='menu' and contains(., '{label}')]"
```

It's kind of frustrating...

We can use Locator class to solve that problem
```python
from seleniumactions import Locator

# You can define any parameters to a locator value template
menu_element = Locator(Using.XPATH, "//ul/li[@class='{class_name}' and contains(., '{label}')]")
menu_element.get_by(class_name='menu', label='Foo')
# >>> ("xpath", "//ul/li[@class='menu' and contains(., 'Foo')]")
menu_element.get_by(class_name='active-menu', label='Bar')
# >>> ("xpath", "//ul/li[@class='active-menu' and contains(., 'Bar')]")

# When you forget to pass values you'll get clear error
button = Locator(Using.NAME, '{action}-{foo}')
button.get_by()
# ValueError: get_by method is missing keyword arguments: ['action', 'foo']
button.get_by(action='goto')
# ValueError: get_by method is missing keyword argument: foo

# cool! 🕹 we can play with it!
```


### Examples (advanced)

We can go step further and implement our own custom locators 🚀
```python
# utils/locators.py
from seleniumactions import Using, Locator


class ButtonByLabel(Locator):
    def __init__(self) -> None:
        super().__init__(Using.XPATH, "//button[.='{label}']")


class ButtonSubmit(Locator):
    def __init__(self) -> None:
        super().__init__(Using.XPATH, "//button[@type='submit']")


class LinkByExactText(Locator):
    def __init__(self) -> None:
        super().__init__(Using.XPATH, "//a[.='{text}']")


class LinkByContainedText(Locator):
    def __init__(self) -> None:
        super().__init__(Using.XPATH, "//a[contains(.='{text}')]")


class HeaderByExactText(Locator):
    def __init__(self) -> None:
        super().__init__(Using.XPATH, "//h*[.='{text}']")

# You can easly match the composition of webapp you're testing with simple classes that inherit from Locator

class Locators:
    # for importing and better intellisence in other modules
    link = LinkByExactText()
    link_contains = LinkByContainedText()
    button = ButtonByLabel()
    submit_button = ButtonSubmit()
    header = HeaderByExactText()


###########################################
# test.py
from utlis.locators import Locators as Loc

# raw calling
actions.click(Loc.link.get_by(text='HOME'))
actions.wait_for(Loc.header.get_by(text='Welcome home!'))
actions.click(Loc.link_contains.get_by(text='see more...'))
actions.click(Loc.button.get_by(label='Next'))
actions.click(Loc.submit_button.get_by())

# or for more readability
home_link = Loc.link.get_by(text='HOME')
see_more_link = Loc.link_contains.get_by(text='see more...')
next_button = Loc.button.get_by(label='Next')
submit_button = Loc.submit_button.get_by()
home_header = Loc.header.get_by(text='Welcome home!')

actions.click(home_button)
actions.wait_for(home_header)
actions.click(see_more_link)
actions.click(next_button)
actions.click(submit_button)

# SUPER DRY!
```



---
todo clean

readme here 👀

Flake8 `flake8 --ignore=E701,E401,E704,F403 --max-line-length=120 --exclude=__init__.py,env/,setup.py,wip.py`
build `pytest && python -m build`
publis `twine upload dist/*`

todo:

github actions -> flake -> test -> build -> publish when tagged
