mirror of
https://github.com/status-im/status-mobile.git
synced 2025-02-12 00:27:42 +00:00
497af38a7f
Replace all the usage of the button without component Use quo Fix tests List item in multiaccounts Use list items in contacts Fix welcome screen button Experiment long press Big list item Remove old bottom sheet Use bottom sheet Keycard Add error to list item Stickers panel button Images panel Fix z-index in profile Fix android crash Fix signing list item Try fixing test iOs gas sheet keyboard Disable root alert in e2e keycard signing sheet height Clean up bottom sheet events Replace flat list in profile Memorise the manual-close value for bottom sheet Mailserver QR scanner Fix e2e tests E2e fix 2 Fix e2e 3 Remove extra fn Reduce bridging time for animation Trick android layout Try hooks Fix profile missing ens-name Disable press on control in list-view allow disabling animations in list item Use simple list in wallet assets settings TBD - this screen should be rewritten from scratch. Now on every interaction the full list is re-rendered, also it makes the wallet main screen to re-render. Fix send sheet Handle long press in main thread UI fixes perf Update e2e fix missing user name in image long press Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
301 lines
11 KiB
Python
301 lines
11 KiB
Python
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))
|
|
if direction == 'down':
|
|
self.driver.swipe(500, 1000, 500, 500)
|
|
else:
|
|
self.driver.swipe(500, 500, 500, 1000)
|
|
else:
|
|
raise NoSuchElementException(
|
|
"Device %s: '%s' is not found on the screen" % (self.driver.number, self.name)) from None
|
|
|
|
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
|
|
|
|
@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 + 100, 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):
|
|
text = self.find_element().text
|
|
self.driver.info('%s is %s' % (self.name, text))
|
|
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 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' % 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' % desired_element.name)
|
|
counter += 1
|
|
except (NoSuchElementException, TimeoutException):
|
|
return self.navigate()
|