# rti-mobile-automation

# Appium-based Test Automation Framework for the Apollo Go application

Overview
=============
Require Python 3.8+ version

* Framework is based on the page object & page element models.
* Added Allure as reporting tool.
* Test data stored in JSON file.
* Credentials end environment variables should be taken from .env file.
* Integrated API client.
* Supports verification and asserts with native Python assert and assertpy lib.
* Supports taking screenshots during failure and verification step.
* Supports reruns & parallel execution.
* Internal ui_automation_framework package is used as basic selenium wrapper and driver factory provider.

SETUP:
===
1. Install Xcode. Go to App Store. Search for Xcode

2. Install node and npm: https://www.npmjs.com/get-npm

3. (Optionally) Install latest stable appium desktop client: https://github.com/appium/appium-desktop/releases

4. Install XCUITest:
```sh
brew install libimobiledevice
```

5. Install Carthage:
```sh
brew install carthage
npm install -g ios-deploy
```

6. Install Latest Appium and required dependencies globally using NPM.
Appium is an open source test automation framework for use with native, hybrid and mobile web apps. 
It drives iOS, Android, and Windows apps using the WebDriver protocol.
https://appium.io
Run following command in terminal to install appium.
```
npm install -g appium
```

WD is node.js Webdriver/Selenium 2 client
This library is designed to be a maleable implementation of the webdriver protocol in Node, exposing functionality via a number of programming paradigms.
Run following command in terminal to install wd.
```
npm install wd
```

Appium Doctor checks most of the preconditions for Appium to run successfully. Attempts to diagnose and fix common Node, iOS and Android configuration issues before starting Appium.
Run following command in terminal to install appium-doctor.
```
npm install -g appium-doctor
```

Note : If you want to install a specific version of appium you can run below command.
```
npm install -g appium@1.1x.x
```

7. Download an actual build in .app format and put it into root folder of the framework

8. Launch & start appium

9. Make sure you have all required ".env_sample" variables set and rename it to ".env"
Or create run configuration in PyCharm

10. Make sure local artifactory is used to install ui_automation_framework package:

Put configuration in ~/.config/pip/pip.conf file and make sure VPN is connected
```sh
[global]
index = https://artifacts.apollo.stream/repository/pypi-all/pypi
index-url = https://artifacts.apollo.stream/repository/pypi-all/simple
```
11. Install requirements and dependencies: 
```sh
pip install -r requirements/base.txt
```




RUN:
====

Run specific test suite:
```sh
py.test --html=report.html --self-contained-html  --alluredir=allure_report --disable-pytest-warnings src/tests/gui/test_login.py --reruns 1
```

To run tests in parallel add option:
```
-n {streams_count}
```

To add reruns for failed test:
```
--reruns {reruns_count}
```

REPORTING:
===
Both Allure and the pytest reports are available

Generating allure report:
```sh
make report
```
PyTest report is available after run as report.html file

CONFIGURATION OF THE FRAMEWORK
===
All test-data related configuration is stored under src/test_data/test_data.json file.

    {
      "invalid_password": "1",
      "invalid_email": "dxd@we",
      "non_existing_email": "invalid@gmail.com"
    }

And read the required field with DataLoader:

    data = DataLoader("test_login_data.json")



CREATION OF YOUR OWN UI TEST:
===
All application-related code is stored in src/ directory.
Pages, actions and business logic are stored in src/pages/ directory and named accordingly to represented page.
You can use two different approaches in working with pages, the first one is based on usage of internal Selenium wrapper inherited from ui_automation_framework/core/init_driver.py

    @allure.step("verifying successful login with valid credentials")
    def login(self, email, password):
        self.click_element(LoginLocators.LOGIN_BN)
        self.wait_for_element_visible(LoginLocators.EMAIL_FIELD)
        self.click_element(LoginLocators.EMAIL_FIELD)
        self.send_text(email, LoginLocators.EMAIL_FIELD)
        self.click_element(LoginLocators.PW_FIELD)
        self.click_element(password, LoginLocators.PW_FIELD)
        self.click_element(LoginLocators.LOGIN_FORM_BN)
        return self

Or support page element model:

    @property
    def bn_logout(self):
    return ButtonUIElement(self.driver, LeftMenuLocators.LOG_OUT_BN, "Log Out")

    @property
    def bn_close_menu(self):
        return ButtonUIElement(self.driver, LeftMenuLocators.CLOSE_MENU_BN, "Close Menu X icon")

    def click_logout_btn(self):
        self.bn_logout.click()
        return self

    def click_close_menu_icon(self):
        self.bn_close_menu.click()
        return self

UI elements described in app/locators/ directory  in JSON files named accordingly to represented page. Locator description includes related page, location strategy (xpath, css..) and locator itself.

    class LoginLocators:
        LOGO_IMG = (MobileBy.IOS_CLASS_CHAIN,
                    "**/XCUIElementTypeOther[`label == \"Welcome to Real Time Insights\"`]/XCUIElementTypeOther")
        EMAIL_FIELD = (MobileBy.IOS_CLASS_CHAIN, "**/XCUIElementTypeTextField[`name == \"Email\"`]")

Tests are stored in app/tests/GUI/ dir and describe test flow, test file should contain 'test' in file name in order to pytest can collect it (test_login.py)':

    @allure.story("POC scope")
    @allure.severity(allure.severity_level.CRITICAL)
    @allure.title("Verifying all users notifications")
    def test_login(self):
        self.loginPage.login(login, password)
        assert self.loginPage.is_logged_in(), "user is logged in"

Also, assertpy library is available as replacement for native assert:
   
    @allure.title("Sign in with the single artist user")
    @allure.description("User should be able to successfully log in")
    @allure.testcase("https://data-analytics.atlassian.net/browse/AMA-1675")
    @pytest.mark.smoke
    def test_single_user_login(self):
         self.login_page.go_to_login_form().enter_login_credentials(self.valid_login, self.invalid_pw).click_login()
         assert_that(self.login_page.is_wrong_credentials_error_message_shown(), "Error message wasn't shown").is_true()

CREATION OF YOUR OWN API TEST:
===
### ! API package is TBD, please see examples in apollo-go-automation framework ! 
API helper available under framework/api/ directory. Just add your endpoint description in appropriate client and don't forget to import it in your test.

    @allure.step("Getting portal playlists data from /spotify/filtr-track-playlists/?")
    def get_spotify_playlists_data_portal(self, *parameters, token):
        headers = {'authorization': 'Bearer {}'.format(token)}
        api_string = '/apollo-api/spotify/filtr-track-playlists/?'

        params_list = "&".join(parameters)
        resp = self._s.get(self.host_endpoint + api_string + params_list,
                           headers=headers, timeout=self.timeout)
        self.log.info(self.host_endpoint + api_string + params_list)
        response = {'status_code': resp.status_code, 'body': resp.json()}
        return response


    portal_start = self.api_client.get_spotify_playlists_data_portal("trackID=2ksOAxtIxY8yElEWw8RhgK",
                                                                     "limit=100",
                                                                     "offset=100",
                                                                     "market=us",
                                                                     token=self.token_mobile)
    total_potal = portal_start['body']['pagination']['total']

TAKING SCREENSHOTS
===
Screenshots will be automatically taken during failure and attached to the allure report.
All screenshots will be saved under app/screenshots/ dir.


CODE RECOMMENDATIONS AND STYLE GUIDE:
====================================
1. Overall code style is following PEP8 (https://www.python.org/dev/peps/pep-0008/)
2. Tests are groped in sub-folder: api for API and data validation checks, and gui for UI E2E scenarios
3. UI test scripts built with usage of PageObject pattern
4. API-tests are written in procedure-like style to support parametrization
5. Code separated into 4 the layers: locators class describing app’s elements with class-chain(iOS) or xpath locators, elements layer describing UI component and contains appropriate methods(waits, inputs, etc..), page layer with business logic describing interactions with the app, test layer manipulating with the business logic to perform actual verifications.
6. Tests are grouped into separate files/suites accordingly to existing features
7. To save time for execution you can logically group several test cases on the same feature into E2E scenario
8. General test structure is AAA (Arrange, Act, Assert) test data on top, then business actions, then verifications
9. For the test data use the API calls, put preparation methods into helpers/api_data_provider.py
10. Move common data preparation out of the test on top of the test class in test fixtures (or into conftest.py)
11. Avoid usage of hardcoded values and “magic numbers”, create variables with meaningful names to represent them or use config.json as data storage
12. Use fixtures for tests setup/teardown
13. Each UI test should use @allure decorators to include actual name, description and links to test cases in Jira
14. For iOS tests usage of “class-chain” is a MUST as time saving strategy, xPath can be used only for exceptions like locating elements with axises or usage of ‘NOT’ boolean expression (consider creation of a ticket for adding test-id by developers team)
15. Naming: use meaningful nouns for objects, elements and variables, verbs with prefixes for methods and add "is"/"are" for verification methods
16. Use domain language from the manual test-cases for naming
17. Code structure on the pages layer is: dynamic elements description as constants, actions steps, getters/setters, verifications, common steps
18. Make sure verifications steps return booleans, getters return values and actions return class object to support chain of invocations on a same page(if action leads to another logical page - return object of this page)
19. Don't hesitate to comment workarounds and complex business logic
20. Use chain of invocations in act section of a test case to reduce code lines
21. Remove unused code
22. Naming for API calls in /api folder is a full name of endpoint path including version (e.g GET /apollo-api/v1/apple-music/track/previous-playlists/ > get_v1_apple_music_track_previous_playlists(self, *parameters, token))
23. USE LINTERS BEFORE CREATION OF A PR
24. Always create appropriate branch once you start work on a new task
25. Create and push appropriate tag after release (ex.: git tag v4.0.9)
26. Use docstrings with detailed descriptions for helpers and utils methods
27. Don't create silent verifications, make sure each verification step contains clear error message in case of failure