2018-05-02 16:01:17 +00:00
|
|
|
import base64
|
|
|
|
from io import BytesIO
|
|
|
|
import os
|
2018-05-16 19:59:36 +00:00
|
|
|
|
|
|
|
import time
|
2018-05-02 16:01:17 +00:00
|
|
|
from PIL import Image, ImageChops
|
2018-01-14 17:43:36 +00:00
|
|
|
from appium.webdriver.common.mobileby import MobileBy
|
2018-05-16 19:59:36 +00:00
|
|
|
from appium.webdriver.common.touch_action import TouchAction
|
2018-01-26 11:07:09 +00:00
|
|
|
from selenium.common.exceptions import NoSuchElementException
|
|
|
|
from selenium.common.exceptions import TimeoutException
|
2017-08-28 10:02:20 +00:00
|
|
|
from selenium.webdriver.support.wait import WebDriverWait
|
|
|
|
from selenium.webdriver.support import expected_conditions
|
2018-01-26 11:07:09 +00:00
|
|
|
from tests import info
|
2017-08-28 10:02:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BaseElement(object):
|
|
|
|
class Locator(object):
|
|
|
|
|
|
|
|
def __init__(self, by, value):
|
|
|
|
self.by = by
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def xpath_selector(locator, value):
|
2017-09-13 14:34:42 +00:00
|
|
|
return locator(MobileBy.XPATH, value)
|
2017-08-28 10:02:20 +00:00
|
|
|
|
2017-09-11 10:43:42 +00:00
|
|
|
@classmethod
|
|
|
|
def accessibility_id(locator, value):
|
|
|
|
return locator(MobileBy.ACCESSIBILITY_ID, value)
|
|
|
|
|
2018-04-26 06:22:11 +00:00
|
|
|
@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 + '")]')
|
|
|
|
|
2017-08-28 10:02:20 +00:00
|
|
|
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):
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Looking for %s' % self.name)
|
|
|
|
try:
|
|
|
|
return self.driver.find_element(self.locator.by, self.locator.value)
|
|
|
|
except NoSuchElementException as exception:
|
|
|
|
exception.msg = "'%s' is not found on screen, using: '%s'" % (self.name, self.locator)
|
|
|
|
raise exception
|
2017-08-28 10:02:20 +00:00
|
|
|
|
2017-10-05 19:41:17 +00:00
|
|
|
def find_elements(self):
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Looking for %s' % self.name)
|
2017-10-05 19:41:17 +00:00
|
|
|
return self.driver.find_elements(self.locator.by, self.locator.value)
|
|
|
|
|
2017-08-31 13:39:41 +00:00
|
|
|
def wait_for_element(self, seconds=10):
|
2018-01-26 11:07:09 +00:00
|
|
|
try:
|
2018-05-02 16:01:17 +00:00
|
|
|
return WebDriverWait(self.driver, seconds) \
|
2018-01-26 11:07:09 +00:00
|
|
|
.until(expected_conditions.presence_of_element_located((self.locator.by, self.locator.value)))
|
|
|
|
except TimeoutException as exception:
|
|
|
|
exception.msg = "'%s' is not found on screen, using: '%s', during '%s' seconds" % (self.name, self.locator,
|
|
|
|
seconds)
|
|
|
|
raise exception
|
2017-08-28 10:02:20 +00:00
|
|
|
|
2018-03-15 20:01:08 +00:00
|
|
|
def wait_for_visibility_of_element(self, seconds=10):
|
|
|
|
try:
|
2018-05-02 16:01:17 +00:00
|
|
|
return WebDriverWait(self.driver, seconds) \
|
2018-03-15 20:01:08 +00:00
|
|
|
.until(expected_conditions.visibility_of_element_located((self.locator.by, self.locator.value)))
|
|
|
|
except TimeoutException as exception:
|
|
|
|
exception.msg = "'%s' is not found on screen, using: '%s', during '%s' seconds" % (self.name, self.locator,
|
|
|
|
seconds)
|
|
|
|
raise exception
|
|
|
|
|
2018-05-25 17:29:07 +00:00
|
|
|
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 as exception:
|
|
|
|
exception.msg = "'%s' is still present on screen, using: '%s', during '%s' seconds" % (self.name,
|
|
|
|
self.locator,
|
|
|
|
seconds)
|
|
|
|
raise exception
|
|
|
|
|
2017-08-31 13:39:41 +00:00
|
|
|
def scroll_to_element(self):
|
2018-01-26 11:07:09 +00:00
|
|
|
for _ in range(9):
|
2017-08-31 13:39:41 +00:00
|
|
|
try:
|
2017-10-11 20:10:57 +00:00
|
|
|
return self.find_element()
|
2017-08-31 13:39:41 +00:00
|
|
|
except NoSuchElementException:
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Scrolling down to %s' % self.name)
|
|
|
|
self.driver.swipe(500, 1000, 500, 500)
|
2017-08-31 13:39:41 +00:00
|
|
|
|
2017-09-13 14:34:42 +00:00
|
|
|
def is_element_present(self, sec=5):
|
|
|
|
try:
|
2018-02-09 15:16:07 +00:00
|
|
|
info('Wait for %s' % self.name)
|
2018-03-15 20:01:08 +00:00
|
|
|
return self.wait_for_element(sec)
|
|
|
|
except TimeoutException:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def is_element_displayed(self, sec=5):
|
|
|
|
try:
|
|
|
|
info('Wait for %s' % self.name)
|
|
|
|
return self.wait_for_visibility_of_element(sec)
|
2017-09-13 14:34:42 +00:00
|
|
|
except TimeoutException:
|
|
|
|
return False
|
|
|
|
|
2017-09-21 17:01:04 +00:00
|
|
|
@property
|
|
|
|
def text(self):
|
|
|
|
return self.find_element().text
|
|
|
|
|
2018-05-02 16:01:17 +00:00
|
|
|
@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)))
|
|
|
|
|
2018-05-16 19:59:36 +00:00
|
|
|
def is_element_image_equals_template(self, file_name: str = ''):
|
|
|
|
if file_name:
|
|
|
|
self.template = file_name
|
2018-05-02 16:01:17 +00:00
|
|
|
return not ImageChops.difference(self.image, self.template).getbbox()
|
|
|
|
|
2018-05-16 19:59:36 +00:00
|
|
|
def swipe_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 / 2, start_y=y + height / 2, end_x=x, end_y=y + height / 2)
|
|
|
|
|
|
|
|
def long_press_element(self):
|
|
|
|
element = self.find_element()
|
|
|
|
info('Long press %s' % self.name)
|
|
|
|
action = TouchAction(self.driver)
|
|
|
|
action.long_press(element).release().perform()
|
|
|
|
|
2017-08-28 10:02:20 +00:00
|
|
|
|
|
|
|
class BaseEditBox(BaseElement):
|
|
|
|
|
|
|
|
def __init__(self, driver):
|
|
|
|
super(BaseEditBox, self).__init__(driver)
|
|
|
|
|
|
|
|
def send_keys(self, value):
|
|
|
|
self.find_element().send_keys(value)
|
2018-01-26 11:07:09 +00:00
|
|
|
info("Type '%s' to %s" % (value, self.name))
|
2017-08-28 10:02:20 +00:00
|
|
|
|
2017-10-05 19:41:17 +00:00
|
|
|
def set_value(self, value):
|
|
|
|
self.find_element().set_value(value)
|
2018-01-26 11:07:09 +00:00
|
|
|
info("Type '%s' to %s" % (value, self.name))
|
2017-10-05 19:41:17 +00:00
|
|
|
|
2017-10-30 11:11:58 +00:00
|
|
|
def clear(self):
|
|
|
|
self.find_element().clear()
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Clear text in %s' % self.name)
|
2017-10-30 11:11:58 +00:00
|
|
|
|
2018-01-14 17:43:36 +00:00
|
|
|
def click(self):
|
|
|
|
self.find_element().click()
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Tap on %s' % self.name)
|
2018-01-14 17:43:36 +00:00
|
|
|
|
2018-05-16 19:59:36 +00:00
|
|
|
def delete_last_symbols(self, number_of_symbols_to_delete: int):
|
|
|
|
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):
|
|
|
|
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+100, y=y-50).release().perform()
|
|
|
|
|
|
|
|
def cut_text(self):
|
|
|
|
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()
|
|
|
|
|
2017-08-28 10:02:20 +00:00
|
|
|
|
|
|
|
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
|
2018-01-26 11:07:09 +00:00
|
|
|
info('%s is %s' % (self.name, text))
|
2017-09-21 17:01:04 +00:00
|
|
|
return text
|
2017-08-28 10:02:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
class BaseButton(BaseElement):
|
|
|
|
|
|
|
|
def __init__(self, driver):
|
|
|
|
super(BaseButton, self).__init__(driver)
|
|
|
|
|
|
|
|
def click(self):
|
|
|
|
self.find_element().click()
|
2018-01-26 11:07:09 +00:00
|
|
|
info('Tap on %s' % self.name)
|
2017-08-28 10:02:20 +00:00
|
|
|
return self.navigate()
|
2018-02-09 15:16:07 +00:00
|
|
|
|
2018-02-19 11:51:53 +00:00
|
|
|
def click_until_presence_of_element(self, desired_element, attempts=3):
|
2018-02-09 15:16:07 +00:00
|
|
|
counter = 0
|
2018-03-28 10:21:39 +00:00
|
|
|
while not desired_element.is_element_present(1) and counter <= attempts:
|
2018-02-09 15:16:07 +00:00
|
|
|
try:
|
|
|
|
info('Tap on %s' % self.name)
|
|
|
|
self.find_element().click()
|
|
|
|
info('Wait for %s' % desired_element.name)
|
|
|
|
desired_element.wait_for_element(5)
|
|
|
|
return self.navigate()
|
|
|
|
except (NoSuchElementException, TimeoutException):
|
|
|
|
counter += 1
|