Skip to content

Commit 6a3e2e7

Browse files
committed
Initial commit for POC of Appium app-based testing in a python object oriented structure
1 parent 236f92a commit 6a3e2e7

File tree

17 files changed

+445
-1
lines changed

17 files changed

+445
-1
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
# mobile-appium-framework-py
1+
# mobile-appium-framework-py
2+
Make sure all requirements are installed by running `pip install -r requirements.txt`
3+
4+
To run the example test, enter `pytest --is_headless=False tests/ios_ui/test_filelist.py -s` in command line.

android_objects/__init__.py

Whitespace-only changes.

ios_objects/__init__.py

Whitespace-only changes.

ios_objects/ios_page_locators.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Created December 29th, 2020 by Alysha Kester-Terry
2+
This Base Page object locator strategy was gleaned with much gratitude from
3+
http://elementalselenium.com/tips/9-use-a-base-page-object
4+
"""
5+
6+
7+
class BasePageLocators(object):
8+
"""Initializes the driver for use on all other pages and defines objects that are on almost every page"""
9+
10+
def __init__(self, driver):
11+
self.driver = driver
12+
13+
def alert_type_box(self):
14+
return self.driver.find_element_by_class_name("XCUIElementTypeAlert")
15+
16+
def alert_box(self):
17+
return self.driver.find_element_by_class_name("XCUIElementTypeScrollView")
18+
19+
20+
class IosMemberListPageLocators(BasePageLocators):
21+
"""iOS Member List Page Locators"""
22+
23+
def member_list_page_title(self):
24+
return self.driver.find_element_by_name('D+D Members')
25+
26+
def member_list(self):
27+
return self.driver.find_elements_by_name('Picture')
28+
29+
30+
class IosMemberDetailPageLocators(BasePageLocators):
31+
"""iOS Member Detail Page Locators"""
32+
33+
def phone_number(self):
34+
return self.driver.find_element_by_name('Phone')
35+
36+
def profile_image(self):
37+
return self.driver.find_element_by_name('Profile image')
38+
39+
def email(self):
40+
return self.driver.find_element_by_name('Email')
41+
42+
def bio(self):
43+
return self.driver.find_element_by_name('Bio')

ios_objects/ios_pages.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"""Created December 15th, 2020 by Alysha Kester-Terry """
2+
3+
import datetime
4+
5+
import pytest
6+
from appium.webdriver.common.touch_action import TouchAction
7+
from selenium.common.exceptions import NoSuchElementException
8+
from selenium.webdriver.support import expected_conditions
9+
from selenium.webdriver.support.wait import WebDriverWait
10+
11+
from ios_objects import ios_page_locators
12+
from setup_helpers import driver_setup
13+
from tests import conftest
14+
15+
created_date = str(datetime.datetime.utcnow().strftime("%m-%d-%H%M"))
16+
file_name = 'test-reports/screenshots/ios/' + created_date
17+
default_wait = 15
18+
19+
20+
class BasePage(object):
21+
"""Base class to initialize the page class that will be called from all pages"""
22+
23+
def __init__(self, driver):
24+
self.driver = driver
25+
26+
# Functional/Interaction with Page Elements
27+
28+
def take_screenshot(self, name=None):
29+
"""Takes a screenshot and saves it to test-reports"""
30+
if name is None:
31+
name = 'screenshot'
32+
screenshot = self.driver.get_screenshot_as_png()
33+
screenshot_file = open(file_name + name + '.png', "wb")
34+
screenshot_file.write(screenshot)
35+
screenshot_file.close()
36+
37+
def enter_text(self, element, text_to_enter):
38+
element.clear()
39+
element.send_keys(text_to_enter)
40+
41+
def get_element_text(self, element):
42+
return element.text
43+
44+
def tap_element(self, element):
45+
element.click()
46+
self.wait_for_seconds(2)
47+
48+
def wait_for_seconds(self, seconds=3):
49+
conftest.max_sleep(seconds)
50+
51+
def wait_for_element(self, element):
52+
try:
53+
print('Waiting for element...')
54+
WebDriverWait(driver=self.driver,
55+
timeout=default_wait,
56+
poll_frequency=2).until(
57+
expected_conditions.visibility_of(element))
58+
print('The element is found? {}'.format(element.is_displayed()))
59+
except NoSuchElementException as n:
60+
print('Element was not found: {}'.format(element), n)
61+
exists = self.element_exists(element)
62+
print('The element exists? {}'.format(exists))
63+
assert exists
64+
65+
def wait_for_invisibility(self, element):
66+
print('Waiting for invisibility of element...')
67+
WebDriverWait(driver=self.driver,
68+
timeout=default_wait,
69+
poll_frequency=2).until(
70+
expected_conditions.invisibility_of_element(element))
71+
exists = self.element_exists(element)
72+
print('The element exists? {}'.format(exists))
73+
assert exists is False
74+
75+
def element_exists(self, element):
76+
return element.is_displayed()
77+
78+
def swipe_up(self):
79+
print('Trying to swipe up!')
80+
actions = TouchAction(self.driver)
81+
actions.long_press(x=180, y=510).move_to(x=150, y=250).release().perform()
82+
83+
def get_page_src_info(self):
84+
source_hierarchy = self.driver.page_source
85+
print(source_hierarchy)
86+
87+
def process_failure(self, error):
88+
self.get_page_src_info()
89+
pytest.fail('The test failed. {}'.format(error), True)
90+
91+
def tear_down(self, failure):
92+
if failure is None:
93+
self.take_screenshot('Pass')
94+
else:
95+
self.take_screenshot('Failed')
96+
self.driver.quit()
97+
driver_setup.tear_down()
98+
99+
100+
class IosMemberListPage(BasePage):
101+
"""Member List Page Action Methods"""
102+
103+
def wait_for_load_complete(self):
104+
self.wait_for_seconds(2)
105+
title = self.member_list_title_exists()
106+
self.take_screenshot('MemberListScreen')
107+
while title is False:
108+
print('List page is initiated? {}'.format(title) + ' waiting...')
109+
self.wait_for_seconds(1)
110+
print('is page is initiated? {}'.format(title))
111+
112+
def member_list_title_exists(self):
113+
element = ios_page_locators.IosMemberListPageLocators.member_list_page_title(self)
114+
return self.element_exists(element)
115+
116+
def get_member_list_title(self):
117+
element = ios_page_locators.IosMemberListPageLocators.member_list_page_title(self)
118+
return self.get_element_text(element)
119+
120+
def tap_member_name(self, member_name):
121+
member_element_list = ios_page_locators.IosMemberListPageLocators.member_list(self)
122+
for member_element in member_element_list:
123+
this_member = self.get_element_text(member_element).lower()
124+
if member_name.lower() in this_member:
125+
print('Found member: {}'.format(this_member))
126+
self.tap_element(member_element)
127+
break
128+
129+
130+
class IosMemberDetailPage(BasePage):
131+
"""Member Detail Page Action Methods"""
132+
133+
def wait_for_load_complete(self):
134+
initiated = self.member_bio_exists()
135+
print('Member detail page is initiated? {}'.format(initiated))
136+
self.take_screenshot('MemberDetailScreen')
137+
138+
def member_picture_exists(self):
139+
element = ios_page_locators.IosMemberDetailPageLocators.profile_image(self)
140+
return self.element_exists(element)
141+
142+
def member_bio_exists(self):
143+
element = ios_page_locators.IosMemberDetailPageLocators.bio(self)
144+
return self.element_exists(element)
145+
146+
def get_member_bio(self):
147+
element = ios_page_locators.IosMemberDetailPageLocators.bio(self)
148+
return self.get_element_text(element)

requirements.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
nodejs
2+
datetime~=4.3
3+
pytest~=6.0.1
4+
Appium-Python-Client~=1.0.2
5+
Pillow
6+
pybase64
7+
selenium~=3.141.0

setup_helpers/PlatformType.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""To define the platform type"""
2+
import enum
3+
4+
5+
class PlatformType(enum.Enum):
6+
"""To define a standardized platform type"""
7+
ios = 1
8+
android = 2

setup_helpers/__init__.py

Whitespace-only changes.

setup_helpers/driver_setup.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""To set up the appium driver by Platform Type"""
2+
"""Created December 15th, 2020 by Alysha Kester-Terry """
3+
import os
4+
5+
from appium import webdriver
6+
from appium.webdriver.appium_service import AppiumService
7+
8+
appium_service = AppiumService()
9+
10+
11+
def get_app_type(PlatformType):
12+
"""To Define the platform type for your app"""
13+
switcher = {
14+
PlatformType.ios: 'iOS',
15+
PlatformType.android: 'Android'
16+
}
17+
print(switcher.get(PlatformType), 'Invalid app type.')
18+
return switcher.get(PlatformType)
19+
20+
21+
def get_desired_caps(PlatformType, is_headless=False, app_path=None, device_name=None):
22+
"""To Define the desired capabilities type for your app
23+
:param PlatformType: iOS or Android
24+
:param is_headless: whether to run headless
25+
:param app_path: explicit path of app file
26+
:param device_name: iPhone name or Android emulator name
27+
"""
28+
platform_type = get_app_type(PlatformType)
29+
lower_app_type = platform_type.lower()
30+
if app_path is None:
31+
app_path = get_app_path(PlatformType)
32+
print('\nThe App Path we found: {}'.format(app_path))
33+
desired_caps = None
34+
if lower_app_type == 'ios':
35+
if device_name is None:
36+
device_name = 'iPhone 11'
37+
desired_caps = dict(
38+
platformName='iOS',
39+
platformVersion='14.1',
40+
deviceName=device_name,
41+
automationName='XCUITest',
42+
sendKeyStrategy='grouped',
43+
app=app_path,
44+
elementResponseAttributes=True,
45+
isAutomationEnabled=True,
46+
autoAcceptAlerts=False,
47+
autoDismissAlerts=False,
48+
connectHardwareKeyboard=True,
49+
isHeadless=bool(is_headless),
50+
showXcodeLog=True
51+
)
52+
elif lower_app_type == 'android':
53+
if device_name is None:
54+
device_name = 'S7 Edge API 29'
55+
avd_name = device_name.replace(' ', '_')
56+
desired_caps = dict(
57+
platformName='Android',
58+
deviceName=device_name,
59+
avd=avd_name,
60+
automationName='UIAutomator2',
61+
autoGrantPermissions=False,
62+
skipDeviceInitialization=False,
63+
audioPlayback=False,
64+
skipLogcatCapture=False,
65+
app=app_path,
66+
isHeadless=bool(is_headless)
67+
)
68+
print('DESIRED CAPABILITIES: {}'.format(desired_caps))
69+
return desired_caps
70+
71+
72+
def __start_service():
73+
appium_service.start()
74+
print('Appium is running? {}'.format(appium_service.is_running))
75+
print('Appium is listening? {}'.format(appium_service.is_listening))
76+
77+
78+
def get_driver(desired_caps):
79+
running = appium_service.is_running
80+
listening = appium_service.is_listening
81+
print('Appium is running? {}'.format(running))
82+
print('Appium is listening? {}'.format(listening))
83+
if running or listening is False:
84+
__start_service()
85+
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
86+
desired_capabilities=desired_caps)
87+
return driver
88+
89+
90+
def get_app_path(PlatformType):
91+
global file_path
92+
platform_type = get_app_type(PlatformType)
93+
print('The App Type is ... {}'.format(platform_type.lower()))
94+
lower_platform_name = platform_type.lower()
95+
print('\n Getting a dynamic app path')
96+
directory_path = os.path.abspath("temp_app_files")
97+
for root, dirs, files in os.walk(directory_path):
98+
print(files)
99+
for file in files:
100+
print('This file is: {}'.format(file))
101+
if 'ios' in lower_platform_name.lower():
102+
if file.endswith('.zip') or file.endswith('.app'):
103+
file_path = directory_path + '/' + file
104+
print('Found file: {}'.format(file_path))
105+
break
106+
else:
107+
if file.endswith('.apk'):
108+
file_path = directory_path + '/' + file
109+
print('Found file: {}'.format(file_path))
110+
break
111+
return os.path.abspath(file_path)
112+
113+
114+
def tear_down():
115+
appium_service.stop()
116+
print('Appium is running? {}'.format(appium_service.is_running))
117+
print('Appium is listening? {}'.format(appium_service.is_listening))

temp_app_files/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)