Metadata-Version: 2.1
Name: citronella
Version: 0.0.5
Summary: Webdriver Extension with Page Object Wrapper
Home-page: https://github.com/heyclore/citronella
Author: heyclore
Author-email: cloore@gmail.com
License: MIT
Project-URL: Source, https://github.com/heyclore/citronella/tree/main/python
Keywords: test,unittest,pytest,webdriver,appium,selenium
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Testing
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Requires-Python: ~=3.7
Description-Content-Type: text/markdown
Requires-Dist: selenium

# Citronella

[![PyPI version](https://badge.fury.io/py/citronella.svg)](https://badge.fury.io/py/citronella)
[![Downloads](https://pepy.tech/badge/citronella)](https://pepy.tech/project/citronella)

webdriver extension with a page object wrapper.

![alt terminal](https://github.com/heyclore/citronella/blob/main/python/screenshot/terminal.png?raw=true)
![alt pytest-html](https://github.com/heyclore/citronella/blob/main/python/screenshot/pytest_html.png?raw=true)
![alt github-action](https://github.com/heyclore/citronella/blob/main/python/screenshot/github_action.png?raw=true)

## Example Tests
Even though this module is mainly designed for the page object model, it can also be used without it for quick prototyping or mockups, etc.
```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from citronella import WebPage


driver = webdriver.Chrome()

web = WebPage(driver, webdriver_wait=20, logger=False)
web.driver.get('https://pypi.org/')

web.locate(By.ID, 'search').get_element().send_keys('citronella')
web.locate(By.XPATH, '//button[@type="submit"]/i').get_element().click()

elements = web.locate(By.XPATH, '//span[@class="package-snippet__name"]')
if elements.ec_visibility_of_all_elements_located():
    results = elements.get_elements()
    text_lists = [x.text for x in results]
```



### Selenium

```python
import pytest
from Pages.home.home_page import HomePage


class TestNavigationMenu:

    def test_help_page(self, web):
        web.driver.get('https://pypi.org/')
        web.page_object(HomePage)

        web.page.help_button.click()
        assert 'Help' in web.driver.title

    def test_sponsors_page(self, web):
        web.page.sponsors_button.click()
        assert 'Sponsors' in web.driver.title

    def test_login_page(self, web):
        web.page.login_button.click()
        assert 'Log' in web.driver.title

    def test_register_page(self, web):
        web.page.register_button.click()
        assert 'Create' in web.driver.title
```

### Appium

```python
import pytest
from Pages.api_demos_page import ApiDemosPage


class TestNavigationMenu:

    def test_accessibility_page(self, web):
        web.page_object(ApiDemosPage)

        web.page.accessibility_button.click()
        assert web.page.accessibility_node_provider_button.get_element().is_visible()

    def test_animation_page(self, web):
        web.back
        web.page.animation_button.click()
        assert web.page.cloning_button.get_element().is_visible()

    def test_app_page(self, web):
        web.back
        web.page.app_button.click()
        assert web.page.activity_button.get_element().is_visible()

    def test_os_page(self, web):
        web.back
        web.page.os_button.click()
        assert web.page.morse_code_button.get_element().is_visible()
```
___
## Install Package

```bash
pip install citronella
```

___
## Documentation

There are only three modules imported in this package:

* The first module is for conftest.py.

### Selenium

```python
import pytest
from selenium import webdriver
from citronella import WebPage


@pytest.fixture(autouse=True, scope='class')
def web(request):
    driver = webdriver.Chrome()
    yield WebPage(driver)
    driver.quit()
```

### Appium

```python
import pytest
import os
from appium import webdriver
from appium.options.android import UiAutomator2Options
from citronella import WebPage


@pytest.fixture(autouse='true', scope='class')
def web(request):
    options = UiAutomator2Options()
    options.platformName = 'Android'
    options.app = os.getcwd() + '/APK/ApiDemos-debug.apk.zip'
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', options=options)
    yield WebPage(driver)
    driver.quit()
```

* The second and third modules are for the page object model.

[Check out this link for more information on the Page Object Design.](https://github.com/heyclore/citronella/tree/main/python/example#readme)

### Selenium

```python
from selenium.webdriver.common.by import By
from citronella import Ui, PlaceholderPage
from Pages.component.HeaderMenu import HeaderMenu


class HomePage(HeaderMenu):

    def some_button(self):
        return Ui(By.XPATH, '//a[@name="foo"]')

    def search_input(self):
        return Ui(By.ID, 'search')

    def search_button(self):
        from Pages.SearchPage import SearchPage
        return Ui(By.NAME, 'search-button', SearchPage)

    def link_to_somewhere_currently_dont_have_page_object(self):
        return Ui(By.NAME, 'search-button', PlaceholderPage)
```

### Appium

```python
from appium.webdriver.common.appiumby import AppiumBy
from citronella import Ui, PlaceholderPage
from Pages.component.HeaderMenu import HeaderMenu


class HomePage(HeaderMenu):

    def some_button(self):
        return Ui(AppiumBy.XPATH, '//a[@name="foo"]')

    def search_input(self):
        return Ui(AppiumBy.ACCESSIBILITY_ID, 'search')

    def search_button(self):
        from Pages.SearchPage import SearchPage
        return Ui(AppiumBy.ID, 'search-button', SearchPage)

    def link_to_somewhere_currently_dont_have_page_object(self):
        return Ui(AppiumBy.ACCESSIBILITY_ID, 'search-button', PlaceholderPage)
```

___
## Usage

### citronella.WebPage

###### Args:
- driver / webdriver

###### Kwargs (optional):
- webdriver_wait `number(seconds)`, default value is `10`
- logger `bool`, default value is `True`

###### Method Lists:
| Method Name        | Args*       | Kwargs**         | Note |
| ------------------ |:-----------:|:----------------:|:----:|
| driver             | None        | None             | return selenium `webdriver` object |
| locate             | by, value   | None             | similar as`driver.get_element` args |
| page_object        | Page Object | get_start `bool` | Page Object must contain `ACTIVITY` variable with URL(selenium)/State(appium) if using Kwargs** | 
| page               | None        | None             |      |
| back               | None        | None             |      |
| webdriver_wait     | number(sec) | None             |      |
| ready_state        | number(sec) | None             | execute javascript `document.readyState` manually, default timeout is `0` |
| get_window_size    | None        | None             |      |
| sleep              | number(sec) | None             |      |

### citronella.ui / citronella.WebUi

###### Args:
- by
- value
- page_object (optional)

###### Method Lists:
| Method Name   | Args*  | Kwargs**           | Note |
| ------------- |:------:|:------------------:|:----:|
| send_keys     | text   | clear `bool`, return_key `bool` | default value is `False` by default |
| click         | None   | switch_page `bool` | see [test_auth.py](example/selenium/Test/pytest_html_image/test_auth.py) example |
| get_element   | None   | None               |      |
| get_elements  | None   | None               |      |
| ec_element_to_be_clickable | None | None | wrapper of `EC` / `excpected_condition` |
| ec_presence_of_element_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_presence_of_all_elements_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_visibility_of_element_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_visibility_of_all_elements_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_visibility_of_any_elements_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_invisibility_of_element_located | None | None | wrapper of `EC` / `excpected_condition` |
| ec_element_located_to_be_selected | None | None | wrapper of `EC` / `excpected_condition` |


## Testing powered by
<a target="_blank" href="https://www.browserstack.com/"><img width="200" src="https://www.browserstack.com/images/layout/browserstack-logo-600x315.png"></a><br>
[BrowserStack Open-Source Program](https://www.browserstack.com/open-source)
