diff --git a/test/appium/support/base_test_report.py b/test/appium/support/base_test_report.py index 70a230d039..2b2e5cd0e4 100644 --- a/test/appium/support/base_test_report.py +++ b/test/appium/support/base_test_report.py @@ -7,7 +7,7 @@ from sauceclient import SauceException import re from support.test_data import SingleTestData - +from tests.cloudbase_test_api import sauce, apibase class BaseTestReport: TEST_REPORT_DIR = "%s/../report" % os.path.dirname(os.path.abspath(__file__)) @@ -102,7 +102,7 @@ class BaseTestReport: def get_sauce_job_url(self, job_id, first_command=0): token = self.get_sauce_token(job_id) - url = 'https://eu-central-1.saucelabs.com/jobs/%s?auth=%s' % (job_id, token) + url = 'https://%s/jobs/%s?auth=%s' % (apibase, job_id, token) if first_command > 0: url += "#%s" % first_command return url @@ -114,11 +114,10 @@ class BaseTestReport: def get_sauce_final_screenshot_url(self, job_id): token = self.get_sauce_token(job_id) - from tests.cloudbase_test_api import sauce for _ in range(10): try: scr_number = sauce.jobs.get_job_assets(job_id)['screenshots'][-1] - return 'https://assets.eu-central-1.saucelabs.com/jobs/%s/%s?auth=%s' % (job_id, scr_number, token) + return 'https://assets.%s/jobs/%s/%s?auth=%s' % (apibase, job_id, scr_number, token) except SauceException: time.sleep(3) diff --git a/test/appium/support/testrail_report.py b/test/appium/support/testrail_report.py index 882d6c3923..6d2b58685e 100644 --- a/test/appium/support/testrail_report.py +++ b/test/appium/support/testrail_report.py @@ -310,8 +310,12 @@ class TestrailReport(BaseTestReport): return None def get_not_executed_tests(self, test_run_id): - results = self.get("get_tests/%s&status_id=3" % test_run_id) - return [result['case_id'] for result in results["tests"]] + try: + results = self.get("get_tests/%s&status_id=3" % test_run_id) + return [result['case_id'] for result in results["tests"]] + except KeyError: + print('Cannot extract result for %s' % test_run_id) + pass @staticmethod def make_error_with_gh_issue_link(error, issue_id): diff --git a/test/appium/tests/base_test_case.py b/test/appium/tests/base_test_case.py index 04348b44fb..d29e3f338c 100644 --- a/test/appium/tests/base_test_case.py +++ b/test/appium/tests/base_test_case.py @@ -1,4 +1,5 @@ import asyncio +import base64 import logging import re import subprocess @@ -6,6 +7,7 @@ import sys from abc import ABCMeta, abstractmethod from http.client import RemoteDisconnected from os import environ +from re import findall import pytest import requests @@ -16,21 +18,18 @@ from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import WebDriverException from selenium.webdriver.support.wait import WebDriverWait -from tests import transl - +from tests.conftest import option from support.api.network_api import NetworkApi from support.github_report import GithubHtmlReport from tests import test_suite_data, start_threads, appium_container, pytest_config_global -import base64 -from re import findall - -from tests.cloudbase_test_api import sauce +from tests import transl +from tests.cloudbase_test_api import sauce, apibase sauce_username = environ.get('SAUCE_USERNAME') sauce_access_key = environ.get('SAUCE_ACCESS_KEY') -executor_sauce_lab = 'https://%s:%s@ondemand.eu-central-1.saucelabs.com:443/wd/hub' % (sauce_username, sauce_access_key) +executor_sauce_lab = 'https://%s:%s@ondemand.%s:443/wd/hub' % (sauce_username, sauce_access_key, apibase) executor_local = 'http://localhost:4723/wd/hub' @@ -303,6 +302,7 @@ def create_shared_drivers(quantity): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) capabilities = {'maxDuration': 3600} + print('SC Executor: %s' % executor_sauce_lab) drivers = loop.run_until_complete(start_threads(quantity, Driver, drivers, @@ -397,7 +397,10 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase): driver.quit() except WebDriverException: pass - url = 'https://eu-central-1.saucelabs.com/rest/v1/%s/jobs/%s/assets/%s' % (sauce_username, session_id, "log.json") + if option.datacenter == 'eu-central-1': + url = 'https://eu-central-1.saucelabs.com/rest/v1/%s/jobs/%s/assets/%s' % (sauce_username, session_id, "log.json") + else: + url = sauce.jobs.get_job_asset_url(job_id=session_id, filename="log.json") WebDriverWait(driver, 60, 2).until(lambda _: requests_session.get(url).status_code == 200) commands = requests_session.get(url).json() for command in commands: diff --git a/test/appium/tests/cloudbase_test_api.py b/test/appium/tests/cloudbase_test_api.py index 2b58a7cd96..b21a7bdfdd 100644 --- a/test/appium/tests/cloudbase_test_api.py +++ b/test/appium/tests/cloudbase_test_api.py @@ -6,6 +6,8 @@ from time import sleep from sauceclient import SauceClient, SauceException +from tests.conftest import option + try: import http.client as http_client from urllib.parse import urlencode @@ -18,7 +20,12 @@ sauce_username = environ.get('SAUCE_USERNAME') sauce_access_key = environ.get('SAUCE_ACCESS_KEY') sauce = SauceClient(sauce_username, sauce_access_key) -apibase = 'eu-central-1.saucelabs.com' +if option.datacenter == 'us-west-1': + apibase = 'saucelabs.com' +elif option.datacenter == 'eu-central-1': + apibase = 'eu-central-1.saucelabs.com' +else: + raise NotImplementedError("Unknown SauceLabs datacenter") def request(method, url, body=None, content_type='application/json'): @@ -34,9 +41,11 @@ def request(method, url, body=None, content_type='application/json'): response.status, response.reason), response=response) return json.loads(data.decode('utf-8')) + sauce.request = request -def upload_from_url(apk_path = str()): + +def upload_from_url(apk_path=str()): response = requests.get(apk_path, stream=True) response.raise_for_status() apk_name = apk_path.split("/")[-1] @@ -44,7 +53,7 @@ def upload_from_url(apk_path = str()): del response for _ in range(3): try: - requests.post('https://eu-central-1.saucelabs.com/rest/v1/storage/' + requests.post('https://' + apibase + '/rest/v1/storage/' + sauce_username + '/' + apk_name + '?overwrite=true', auth=(sauce_username, sauce_access_key), data=file, diff --git a/test/appium/tests/conftest.py b/test/appium/tests/conftest.py index c13a2d1efd..3c7c1788a9 100644 --- a/test/appium/tests/conftest.py +++ b/test/appium/tests/conftest.py @@ -1,31 +1,26 @@ -import time - -import requests -import pytest import re -from _pytest.runner import runtestprotocol +from dataclasses import dataclass +from datetime import datetime from http.client import RemoteDisconnected +from os import environ + +import pytest +from _pytest.runner import runtestprotocol +from sauceclient import SauceClient, SauceException + +import tests from support.device_stats_db import DeviceStatsDB from support.test_rerun import should_rerun_test from tests import test_suite_data, appium_container -from datetime import datetime -from os import environ -from io import BytesIO -from sauceclient import SauceClient, SauceException -import tests.cloudbase_test_api -from support.api.network_api import NetworkApi -from support.github_report import GithubHtmlReport -from support.testrail_report import TestrailReport -from tests.users import transaction_senders -import tests +# from support.testrail_report import TestrailReport +# +# testrail_report = TestrailReport() sauce_username = environ.get('SAUCE_USERNAME') sauce_access_key = environ.get('SAUCE_ACCESS_KEY') github_token = environ.get('GIT_HUB_TOKEN') sauce = SauceClient(sauce_username, sauce_access_key) -github_report = GithubHtmlReport() -testrail_report = TestrailReport() def pytest_addoption(parser): @@ -41,6 +36,10 @@ def pytest_addoption(parser): action='store', default='sauce', help='Specify environment: local/sauce/api') + parser.addoption('--datacenter', + action='store', + default='eu-central-1', + help='For sauce only: us-west-1, eu-central-1') parser.addoption('--platform_version', action='store', default='8.0', @@ -139,18 +138,37 @@ def pytest_addoption(parser): help='Database name for device stats db') +@dataclass +class Option: + datacenter: str = None + + +option = Option() +testrail_report = None +github_report = None + + def is_master(config): return not hasattr(config, 'workerinput') def is_uploaded(): - stored_files = tests.cloudbase_test_api.sauce.storage.get_stored_files() + from tests.cloudbase_test_api import sauce + stored_files = sauce.storage.get_stored_files() for i in range(len(stored_files['files'])): if stored_files['files'][i]['name'] == test_suite_data.apk_name: return True def pytest_configure(config): + global option + option = config.option + from support.testrail_report import TestrailReport + global testrail_report + testrail_report = TestrailReport() + from support.github_report import GithubHtmlReport + global github_report + github_report = GithubHtmlReport() tests.pytest_config_global = vars(config.option) config.addinivalue_line("markers", "testrail_id(name): empty") if config.getoption('log_steps'): @@ -176,10 +194,11 @@ def pytest_configure(config): description='e2e tests are running') if config.getoption('env') == 'sauce': if not is_uploaded(): + from tests.cloudbase_test_api import sauce, upload_from_url if 'http' in config.getoption('apk'): - tests.cloudbase_test_api.upload_from_url(config.getoption('apk')) + upload_from_url(config.getoption('apk')) else: - tests.cloudbase_test_api.sauce.storage.upload_file(config.getoption('apk')) + sauce.storage.upload_file(config.getoption('apk')) def pytest_unconfigure(config): @@ -251,7 +270,8 @@ def pytest_runtest_makereport(item, call): report.passed) if error: test_suite_data.current_test.testruns[-1].error = final_error - github_report.save_test(test_suite_data.current_test) + from support.github_report import GithubHtmlReport + GithubHtmlReport().save_test(test_suite_data.current_test) if report.when == 'call': current_test = test_suite_data.current_test @@ -290,9 +310,10 @@ def pytest_runtest_makereport(item, call): def update_sauce_jobs(test_name, job_ids, passed): + from tests.cloudbase_test_api import sauce for job_id in job_ids.keys(): try: - tests.cloudbase_test_api.sauce.jobs.update_job(job_id, name=test_name, passed=passed) + sauce.jobs.update_job(job_id, name=test_name, passed=passed) except (RemoteDisconnected, SauceException): pass diff --git a/test/appium/tests/critical/chats/test_1_1_public_chats.py b/test/appium/tests/critical/chats/test_1_1_public_chats.py index 6c537e40b4..9b4b6996cb 100644 --- a/test/appium/tests/critical/chats/test_1_1_public_chats.py +++ b/test/appium/tests/critical/chats/test_1_1_public_chats.py @@ -328,6 +328,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): self.errors.verify_no_errors() @marks.testrail_id(695843) + # moved without edit def test_1_1_chat_text_message_edit_delete_push_disappear(self): self.device_2.just_fyi( "Device 1 sends text message and edits it in 1-1 chat. Device2 checks edited message is shown") @@ -1218,7 +1219,7 @@ class TestEnsStickersMultipleDevicesMerged(MultipleSharedDeviceTestCase): @pytest.mark.xdist_group(name="one_2") @marks.new_ui_critical -class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): +class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase): def prepare_devices(self): self.drivers, self.loop = create_shared_drivers(2) @@ -1227,6 +1228,7 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): self.home_2 = self.device_2.create_user(enable_notifications=True) self.profile_1 = self.home_1.get_profile_view() self.public_key_1, self.default_username_1 = self.home_1.get_public_key_and_username(return_username=True) + self.profile_1.switch_push_notifications() self.public_key_2, self.default_username_2 = self.home_2.get_public_key_and_username(return_username=True) self.profile_1.chats_tab.click() self.chat_1 = self.home_1.add_contact(self.public_key_2) @@ -1271,13 +1273,10 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): self.chat_2.just_fyi("Send messages with non-latin symbols") messages = ['hello', '¿Cómo estás tu año?', 'ё, доброго вечерочка', '® æ ç ♥'] - - for message in messages: - self.chat_2.send_message(message) + [self.chat_2.send_message(message) for message in messages] if not self.chat_1.chat_message_input.is_element_displayed(): self.chat_1.click_system_back_button_until_element_is_shown() self.home_1.get_chat(self.default_username_2).click() - for message in messages: if not self.chat_1.chat_element_by_text(message).is_element_displayed(): self.errors.append("Message with test '%s' was not received" % message) @@ -1287,7 +1286,6 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): sent_time_variants = self.chat_2.convert_device_time_to_chat_timestamp() if timestamp not in sent_time_variants: self.errors.append('Timestamp on message %s does not correspond expected [%s]' % (timestamp, *sent_time_variants)) - for message in [messages[1], messages[2]]: if self.chat_2.chat_element_by_text(message).member_photo.is_element_displayed(): self.errors.append('%s is not stack to 1st(they are sent in less than 5 minutes)!' % message) @@ -1314,3 +1312,41 @@ class TestOneToOneChatMultipleSharedDevices(MultipleSharedDeviceTestCase): if self.chat_2.chat_element_by_text(message).member_photo.is_element_differs_from_template("member.png", diff=5): self.errors.append("Image of user in 1-1 chat is too different from template!") self.errors.verify_no_errors() + + @marks.testrail_id(702733) + def test_1_1_chat_text_message_delete_push_disappear(self): + if not self.chat_1.chat_message_input.is_element_displayed(): + self.home_1.get_chat(self.default_username_2).click() + self.device_2.just_fyi("Verify Device1 can not edit and delete received message from Device2") + message_after_edit_1_1 = 'smth I should edit' + self.chat_2.send_message(message_after_edit_1_1) + chat_1_element = self.chat_1.chat_element_by_text(message_after_edit_1_1) + chat_1_element.long_press_element() + for action in ("edit", "delete-for-everyone"): + if self.chat_1.element_by_translation_id(action).is_element_displayed(): + self.errors.append('Option to %s someone else message available!' % action) + self.home_1.click_system_back_button() + + self.device_2.just_fyi("Delete message for everyone and check it is not shown in chat preview on home") + self.chat_2.delete_message_in_chat(message_after_edit_1_1) + for chat in (self.chat_2, self.chat_1): + if chat.chat_element_by_text(message_after_edit_1_1).is_element_displayed(30): + self.errors.append("Deleted message is shown in chat view for 1-1 chat") + self.chat_1.click_system_back_button_until_element_is_shown() + if self.home_1.element_by_text(message_after_edit_1_1).is_element_displayed(30): + self.errors.append("Deleted message is shown on chat element on home screen") + + self.device_2.just_fyi("Send one more message and check that PN will be deleted with message deletion") + message_to_delete = 'DELETE ME' + self.home_1.put_app_to_background() + self.chat_2.send_message(message_to_delete) + self.home_1.open_notification_bar() + if not self.home_1.get_pn(message_to_delete): + self.errors.append("Push notification doesn't appear") + self.chat_2.delete_message_in_chat(message_to_delete) + pn_to_disappear = self.home_1.get_pn(message_to_delete) + if pn_to_disappear: + if not pn_to_disappear.is_element_disappeared(30): + self.errors.append("Push notification was not removed after initial message deletion") + + self.errors.verify_no_errors() diff --git a/test/appium/views/chat_view.py b/test/appium/views/chat_view.py index 4af9548757..6cc1c98e5c 100644 --- a/test/appium/views/chat_view.py +++ b/test/appium/views/chat_view.py @@ -847,10 +847,11 @@ class ChatView(BaseView): self.chat_message_input.send_keys(message_to_update) self.send_message_button.click() - def delete_message_in_chat(self, message): + def delete_message_in_chat(self, message, everyone=True): self.driver.info("Looking for message '%s' to delete it" % message) self.element_by_text_part(message).long_press_element() - self.element_by_translation_id("delete").click() + for_everyone, for_me = self.element_by_translation_id("delete-for-everyone"), self.element_by_translation_id("delete-for-me") + for_everyone.click() if everyone else for_me.click() def copy_message_text(self, message_text): self.driver.info("Copying '%s' message via long press" % message_text) diff --git a/test/appium/views/profile_view.py b/test/appium/views/profile_view.py index 0aa9417262..b82848f500 100644 --- a/test/appium/views/profile_view.py +++ b/test/appium/views/profile_view.py @@ -372,6 +372,11 @@ class ProfileView(BaseView): chat.confirm_until_presence_of_element(self.add_new_contact_button) self.click_system_back_button_until_element_is_shown() + def switch_push_notifications(self): + self.driver.info("Enabling push notifications via Profile") + self.profile_notifications_button.scroll_and_click() + self.profile_notifications_toggle_button.click() + self.click_system_back_button_until_element_is_shown() def add_custom_network(self, rpc_url: str, name: str, symbol: str, netwrok_id:str): self.driver.info("## Add custom network", device=False)