Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ You can use this test automation framework to write

3. __Appium , WinAppDriver__ and Python scripts for __windows automation__

3. __API automation__ scripts to test endpoints of your web/mobile/desktop applications
4. __API automation__ scripts to test endpoints of your web/mobile/desktop applications

5. __CLI automation__ scripts to test any cli tool or execute commands

 

Expand Down Expand Up @@ -49,6 +51,8 @@ The setup has four parts:
3. [Setup for Mobile/Appium automation](https://github.com/qxf2/qxf2-page-object-model/wiki/Setup#3-setup-for-mobileappium-automation)
4. [Setup for API automation](https://github.com/qxf2/qxf2-page-object-model/wiki/Setup#4-setup-for-api-automation)
5. [Setup for Windows Automation](https://github.com/qxf2/qxf2-page-object-model/wiki/Setup#5-setup-for-windows-automation)
6. **Setup for CLI Automation** - **Note:** No additional setup is required for CLI automation tests.
Once the prerequisites are complete, install the CLI tool you plan to test (if needed). Otherwise, you can run the desired commands directly.

Above links redirects to our github wiki pages.

Expand Down Expand Up @@ -181,6 +185,11 @@ COMMANDS FOR RUNNING TESTS
`python -m pytest tests/test_api_example.py`
**Note:** Ensure the sample `cars-api` is available at `qxf2/cars-api` repository before running the API test.

- **CLI Test**
`python -m pytest test/test_cli_example.py`
**Note:** You can use the `--cli_workdir` and `--cli_timeout` parameters to override the default settings.
By default, `cli_workdir` is set to the root of this repository, and `cli_timeout` is set to 30 seconds.

- **Mobile Test Run on Browserstack/Sauce Labs**
`python -m pytest tests/test_mobile_bitcoin_price --mobile_os_version <android version> --device_name <simulator> --app_path <.apk location on local> --remote_flag Y`
**Note:** For running tests on Browserstack/Sauce Labs, update the Browser_Plaform, Username and AccessKey in `.env.remote` from your Browserstack/Sauce Labs account. Refer our wiki page for more details: [Integrate our Python Selenium automation framework with Cloud Services ](https://github.com/qxf2/qxf2-page-object-model/wiki/Integration-with-Cloud-Services)
Expand Down
13 changes: 13 additions & 0 deletions conf/cli_example_conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
Conf file for cli example test
"""

# echo message
echo_message = "hello-qxf2"

# python version starts with
version_string = "python 3"

# cat file and verify content
cat_file_name="./conf/base_url_conf.py"
cat_expected_strings=["ui_base_url", "api"]
39 changes: 39 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,21 @@ def test_windows_obj(remote_flag, testrail_flag, tesults_flag, test_run_id, appi
{"action": "setSessionStatus", "arguments":
{"status":"failed", "reason": "Exception occured"}}""")

@pytest.fixture
def test_cli_obj(cli_workdir, cli_timeout, testname):
"""
CLI helper fixture
"""
try:
test_cli_obj = PageFactory.get_page_object("Zero cli") # pylint: disable=redefined-outer-name
test_cli_obj.set_params_and_log_file(testname,cli_workdir,cli_timeout)

yield test_cli_obj

except Exception as e: # pylint: disable=broad-exception-caught
print(Logging_Objects.color_text(f"Exception when trying to run test:{__file__}","red"))
print(Logging_Objects.color_text(f"Python says:{str(e)}","red"))

# Fixtures for API Endpoint Auto generation unit tests
@pytest.fixture
def name_generator():
Expand Down Expand Up @@ -560,6 +575,20 @@ def snapshot_update(request):
"pytest fixture for snapshot update"
return request.config.getoption("--snapshot_update")

@pytest.fixture
def cli_workdir(request):
"""
CLI working directory from pytest option
"""
return request.config.getoption("--cli_workdir")

@pytest.fixture
def cli_timeout(request):
"""
CLI command timeout from pytest option
"""
return request.config.getoption("--cli_timeout")

@pytest.fixture
def reportportal_service(request):
"pytest service fixture for reportportal"
Expand Down Expand Up @@ -703,6 +732,7 @@ def pytest_configure(config):
config.addinivalue_line("markers", "GUI: mark a test as part of the GUI regression suite.")
config.addinivalue_line("markers", "API: mark a test as part of the GUI regression suite.")
config.addinivalue_line("markers", "MOBILE: mark a test as part of the GUI regression suite.")
config.addinivalue_line("markers", "CLI: mark a test as part of the CLI regression suite")

def pytest_terminal_summary(terminalreporter):
"add additional section in terminal summary reporting."
Expand Down Expand Up @@ -947,6 +977,15 @@ def pytest_addoption(parser):
action="store_true",
default=False,
help="Update the snapshot instead of comparing")
parser.addoption("--cli_workdir",
action="store",
default=".",
help="Working directory for executing CLI commands")
parser.addoption("--cli_timeout",
action="store",
type=int,
default=30,
help="Timeout (in seconds) for CLI command execution")

except Exception as e: # pylint: disable=broad-exception-caught
print(Logging_Objects.color_text(f"Exception when trying to run test:{__file__}","red"))
Expand Down
81 changes: 81 additions & 0 deletions core_helpers/cli_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
CLI Helper

Helper class to interact with command-line tools.
Follows the same design pattern as other helpers in core_helpers.
"""
from .logging_objects import Logging_Objects
from utils.command_executor import CommandExecutor

class Borg:
"""
The borg design pattern is to share state
#Src: http://code.activestate.com/recipes/66531/
"""
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state

def is_first_time(self):
"Has the child class been invoked before?"
result_flag = False
if len(self.__dict__)==0:
result_flag = True

return result_flag

class CliHelper(Borg, Logging_Objects):
"""
CLI helper to execute terminal commands
"""
def __init__(self, workdir=None, timeout=30):
"Constructor"
Borg.__init__(self)
if self.is_first_time():
self.reset()
self.msg_list = []
self.workdir = workdir
self.timeout = timeout

def reset(self):
"Reset the base page object"
self.result_counter = 0 #Increment whenever success or failure are called
self.pass_counter = 0 #Increment everytime success is called
self.mini_check_counter = 0 #Increment when conditional_write is called
self.mini_check_pass_counter = 0 #Increment when conditional_write is called with True
self.failure_message_list = []
self.failed_scenarios = [] # <- Collect the failed scenarios for prettytable summary
self.screenshot_counter = 1
self.exceptions = []
self.gif_file_name = None
self.rp_logger = None
self.highlight_flag = False

def set_params_and_log_file(self,testname,workdir,timeout):
"set params and call set log file"
self.workdir = workdir
self.timeout = timeout
self.testname = testname
self.set_log_file()

def execute_command(self, command):
"""
Execute a CLI command and return the execution result
"""
return CommandExecutor.run(
command=command,
cwd=self.workdir,
timeout=self.timeout
)

def execute_command_and_verify_success(self, command):
"""
Execute a CLI command and verify successful execution
"""
result_flag = True
result = self.execute_command(command)

if result.exit_code != 0:
result_flag = False

return result_flag,result
29 changes: 23 additions & 6 deletions core_helpers/logging_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def color_text(text, color ="red"):
}
return f"{colors.get(color, colors['reset'])}{text}{colors['reset']}"

def write_test_summary(self):
def write_test_summary(self,cli_test=False):
"Print out a useful, human readable summary"
if self.result_counter==self.pass_counter:
level = "success"
Expand All @@ -36,9 +36,10 @@ def write_test_summary(self):
if self.mini_check_counter > 0:
self.write('Total number of mini-checks=%d'%self.mini_check_counter,level=level)
self.write('Total number of mini-checks passed=%d'%self.mini_check_pass_counter,level=level)
self.make_gif()
if self.gif_file_name is not None:
self.write("Screenshots & GIF created at %s"%self.screenshot_dir)
if cli_test is False:
self.make_gif()
if self.gif_file_name is not None:
self.write("Screenshots & GIF created at %s"%self.screenshot_dir)
if len(self.exceptions) > 0:
self.exceptions = list(set(self.exceptions))
self.write('\n--------USEFUL EXCEPTION--------\n',level="critical")
Expand All @@ -53,7 +54,7 @@ def write(self,msg,level='info', trace_back=None):

def success(self,msg,level='success',pre_format='PASS: '):
"Write out a success message"
self.log_obj.write(pre_format + msg,level)
self.log_obj.write(pre_format + msg + "\n",level)
self.result_counter += 1
self.pass_counter += 1

Expand Down Expand Up @@ -85,7 +86,7 @@ def get_failure_message_list(self):

def failure(self,msg,level='error',pre_format='FAIL: '):
"Write out a failure message"
self.log_obj.write(pre_format + msg,level)
self.log_obj.write(pre_format + msg + "\n",level)
self.result_counter += 1
self.failure_message_list.append(pre_format + msg)
if level.lower() == 'critical':
Expand All @@ -94,3 +95,19 @@ def failure(self,msg,level='error',pre_format='FAIL: '):
def set_rp_logger(self,rp_pytest_service):
"Set the reportportal logger"
self.rp_logger = self.log_obj.setup_rp_logging(rp_pytest_service)

def conditional_write(self,flag,positive,negative,level='info',pre_format=" - "):
"Write out either the positive or the negative message based on flag"
self.mini_check_counter += 1
if level.lower() == "inverse":
if flag is True:
self.write(pre_format + positive,level='error')
else:
self.write(pre_format + negative,level='success')
self.mini_check_pass_counter += 1
else:
if flag is True:
self.write(pre_format + positive,level='success')
self.mini_check_pass_counter += 1
else:
self.write(pre_format + negative,level='error')
9 changes: 0 additions & 9 deletions core_helpers/mobile_app_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,6 @@ def open(self,wait_time=2):
"Visit the page base_url + url"
self.wait(wait_time)

def conditional_write(self,flag,positive,negative,level='debug',pre_format=" - "):
"Write out either the positive or the negative message based on flag"
if flag is True:
self.write(pre_format + positive,level='success')
self.mini_check_pass_counter += 1
if flag is False:
self.write(pre_format + negative,level='error')
self.mini_check_counter += 1

def swipe_to_element(self,scroll_group_locator, search_element_locator, max_swipes=20, direction="up"):
result_flag = False
try:
Expand Down
16 changes: 0 additions & 16 deletions core_helpers/web_app_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,22 +309,6 @@ def read_browser_console_log(self):
self.write(str(e),'critical')
return log

def conditional_write(self,flag,positive,negative,level='info'):
"Write out either the positive or the negative message based on flag"
self.mini_check_counter += 1
if level.lower() == "inverse":
if flag is True:
self.write(positive,level='error')
else:
self.write(negative,level='success')
self.mini_check_pass_counter += 1
else:
if flag is True:
self.write(positive,level='success')
self.mini_check_pass_counter += 1
else:
self.write(negative,level='error')

def start(self):
"Overwrite this method in your Page module if you want to visit a specific URL"
pass
6 changes: 6 additions & 0 deletions page_objects/PageFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def get_page_object(page_name,base_url=url_conf.ui_base_url):
elif page_name in ["zero mobile","zero mobile page"]:
from .zero_mobile_page import Zero_Mobile_Page
test_obj = Zero_Mobile_Page()
elif page_name in ["zero cli","zero cli page"]:
from .zero_cli_page import ZeroCliPage
test_obj = ZeroCliPage()
elif page_name in ["main","main page"]:
from .examples.selenium_tutorial_webpage.tutorial_main_page import Tutorial_Main_Page
test_obj = Tutorial_Main_Page(base_url=base_url)
Expand Down Expand Up @@ -62,4 +65,7 @@ def get_page_object(page_name,base_url=url_conf.ui_base_url):
elif page_name == "notepad":
from page_objects.examples.windows_notepad_app.notepad_home_page import NotepadHomePage
test_obj = NotepadHomePage()
elif page_name == "common_cli_commands":
from page_objects.examples.cli_commands.common_cli_commands import CommonCliCommands
test_obj = CommonCliCommands()
return test_obj
Loading