status-mobile/test/appium/views/base_element.py

311 lines
11 KiB
Python
Raw Normal View History

import base64
from io import BytesIO
import os
import time
from timeit import timeit
from PIL import Image, ImageChops
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.touch_action import TouchAction
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
class BaseElement(object):
class Locator(object):
def __init__(self, by, value):
self.by = by
self.value = value
@classmethod
def xpath_selector(locator, value):
return locator(MobileBy.XPATH, value)
@classmethod
def accessibility_id(locator, value):
return locator(MobileBy.ACCESSIBILITY_ID, value)
@classmethod
def text_selector(locator, text):
return BaseElement.Locator.xpath_selector('//*[@text="' + text + '"]')
@classmethod
def text_part_selector(locator, text):
return BaseElement.Locator.xpath_selector('//*[contains(@text, "' + text + '")]')
@classmethod
def id(locator, value):
return locator(MobileBy.ID, value)
@classmethod
def webview_selector(cls, value):
xpath_expression = '//*[@text="{0}"] | //*[@content-desc="{desc}"]'.format(value, desc=value)
return cls(MobileBy.XPATH, xpath_expression)
def __str__(self, *args, **kwargs):
return "%s:%s" % (self.by, self.value)
def __init__(self, driver):
self.driver = driver
self.locator = None
@property
def name(self):
return self.__class__.__name__
def navigate(self):
return None
def find_element(self):
for _ in range(3):
try:
return self.driver.find_element(self.locator.by, self.locator.value)
except NoSuchElementException:
raise NoSuchElementException(
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
except Exception as exception:
if 'Internal Server Error' in str(exception):
continue
def find_elements(self):
return self.driver.find_elements(self.locator.by, self.locator.value)
def click(self):
self.find_element().click()
self.driver.info('Tap on %s' % self.name)
def wait_for_element(self, seconds=10):
try:
return WebDriverWait(self.driver, seconds) \
.until(expected_conditions.presence_of_element_located((self.locator.by, self.locator.value)))
except TimeoutException:
raise TimeoutException(
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
def wait_for_elements(self, seconds=10):
try:
return WebDriverWait(self.driver, seconds) \
.until(expected_conditions.presence_of_all_elements_located((self.locator.by, self.locator.value)))
except TimeoutException:
raise TimeoutException(
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
def wait_for_visibility_of_element(self, seconds=10, ignored_exceptions=None):
try:
return WebDriverWait(self.driver, seconds, ignored_exceptions=ignored_exceptions) \
.until(expected_conditions.visibility_of_element_located((self.locator.by, self.locator.value)))
except TimeoutException:
raise TimeoutException(
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
def wait_for_invisibility_of_element(self, seconds=10):
try:
return WebDriverWait(self.driver, seconds) \
.until(expected_conditions.invisibility_of_element_located((self.locator.by, self.locator.value)))
except TimeoutException:
raise TimeoutException("Device %s: '%s' is still visible on the screen after %s seconds" % (
self.driver.number, self.name, seconds)) from None
def scroll_to_element(self, depth: int = 9, direction='down'):
for _ in range(depth):
try:
return self.find_element()
except NoSuchElementException:
self.driver.info('Scrolling %s to %s' % (direction, self.name))
size = self.driver.get_window_size()
if direction == 'down':
self.driver.swipe(500, size["height"]*0.4, 500, size["height"]*0.05)
else:
self.driver.swipe(500, size["height"]*0.25, 500, size["height"]*0.8)
else:
raise NoSuchElementException(
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
def scroll_and_click(self):
self.scroll_to_element()
self.click()
def is_element_present(self, sec=5):
try:
self.driver.info('Wait for %s' % self.name)
return self.wait_for_element(sec)
except TimeoutException:
return False
def is_element_displayed(self, sec=5, ignored_exceptions=None):
try:
self.driver.info('Wait for %s' % self.name)
return self.wait_for_visibility_of_element(sec, ignored_exceptions=ignored_exceptions)
except TimeoutException:
return False
2017-09-21 17:01:04 +00:00
@property
def text(self):
return self.find_element().text
@property
def template(self):
try:
return self.__template
except FileNotFoundError:
raise FileNotFoundError('Please add %s image as template' % self.name)
@template.setter
def template(self, value):
self.__template = Image.open(os.sep.join(__file__.split(os.sep)[:-1]) + '/elements_templates/%s' % value)
@property
def image(self):
return Image.open(BytesIO(base64.b64decode(self.find_element().screenshot_as_base64)))
def attribute_value(self, value):
attribute_value = self.find_element().get_attribute(value)
if attribute_value.lower() == 'true':
attribute_state = True
elif attribute_value.lower() == 'false':
attribute_state = False
else:
attribute_state = attribute_value
return attribute_state
# Method-helper for renew screenshots in case if changed
def save_new_screenshot_of_element(self, full_path_to_file: str):
screen = Image.open(BytesIO(base64.b64decode(self.find_element().screenshot_as_base64)))
screen.save(full_path_to_file)
def is_element_image_equals_template(self, file_name: str = ''):
if file_name:
self.template = file_name
return not ImageChops.difference(self.image, self.template).getbbox()
def swipe_left_on_element(self):
element = self.find_element()
location, size = element.location, element.size
x, y = location['x'], location['y']
width, height = size['width'], size['height']
self.driver.swipe(start_x=x + width * 0.75, start_y=y + height / 2, end_x=x, end_y=y + height / 2)
def swipe_to_web_element(self, depth=700):
element = self.find_element()
location = element.location
x, y = location['x'], location['y']
self.driver.swipe(start_x=x, start_y=y, end_x=x, end_y=depth)
def long_press_element(self):
element = self.find_element()
self.driver.info('Long press %s' % self.name)
action = TouchAction(self.driver)
action.long_press(element).release().perform()
def measure_time_before_element_appears(self, max_wait_time=30):
def wrapper():
return self.wait_for_visibility_of_element(max_wait_time)
return timeit(wrapper, number=1)
def measure_time_while_element_is_shown(self, max_wait_time=30):
def wrapper():
return self.wait_for_invisibility_of_element(max_wait_time)
return timeit(wrapper, number=1)
class BaseEditBox(BaseElement):
def __init__(self, driver):
super(BaseEditBox, self).__init__(driver)
def send_keys(self, value):
self.find_element().send_keys(value)
self.driver.info("Type '%s' to %s" % (value, self.name))
def set_value(self, value):
self.find_element().set_value(value)
self.driver.info("Type '%s' to %s" % (value, self.name))
def clear(self):
self.find_element().clear()
self.driver.info('Clear text in %s' % self.name)
def delete_last_symbols(self, number_of_symbols_to_delete: int):
self.driver.info('Delete last %s symbols from %s' % (number_of_symbols_to_delete, self.name))
self.click()
for _ in range(number_of_symbols_to_delete):
time.sleep(1)
self.driver.press_keycode(67)
def paste_text_from_clipboard(self):
self.driver.info('Paste text from clipboard into %s' % self.name)
self.long_press_element()
time.sleep(2)
action = TouchAction(self.driver)
location = self.find_element().location
x, y = location['x'], location['y']
action.press(x=x + 25, y=y - 50).release().perform()
def cut_text(self):
self.driver.info('Cut text in %s' % self.name)
location = self.find_element().location
x, y = location['x'], location['y']
action = TouchAction(self.driver)
action.long_press(x=x, y=y).release().perform()
time.sleep(2)
action.press(x=x + 50, y=y - 50).release().perform()
class BaseText(BaseElement):
def __init__(self, driver):
super(BaseText, self).__init__(driver)
@property
def text(self):
2017-09-21 17:01:04 +00:00
text = self.find_element().text
self.driver.info('%s is %s' % (self.name, text))
2017-09-21 17:01:04 +00:00
return text
class BaseButton(BaseElement):
def __init__(self, driver):
super(BaseButton, self).__init__(driver)
def click(self):
self.find_element().click()
self.driver.info('Tap on %s' % self.name)
return self.navigate()
def wait_and_click(self, time=30):
self.driver.info('Waiting for element %s for max %s sec and click when it is available' % (self.name, time))
self.wait_for_visibility_of_element(time)
self.click()
def click_until_presence_of_element(self, desired_element, attempts=4):
counter = 0
while not desired_element.is_element_present(1) and counter <= attempts:
try:
self.driver.info('Tap on %s' % self.name)
self.find_element().click()
self.driver.info('Wait for %s to be displayed' % desired_element.name)
desired_element.wait_for_element(5)
return self.navigate()
except (NoSuchElementException, TimeoutException):
counter += 1
else:
self.driver.info("%s element not found" % desired_element.name)
def click_until_absense_of_element(self, desired_element, attempts=3):
counter = 0
while desired_element.is_element_present(1) and counter <= attempts:
try:
self.driver.info('Tap on %s' % self.name)
self.find_element().click()
self.driver.info('Wait for %s to disappear' % desired_element.name)
counter += 1
except (NoSuchElementException, TimeoutException):
return self.navigate()