test(Onboarding) Skip closing test app (#11837)

- Closing all app instances by port for local run and by process name for CI
- Threshold for emoji hash decreased in image
comparison 
- Verification point for cropped user icon added
This commit is contained in:
Vladimir Druzhinin 2023-08-10 08:58:50 +02:00 committed by GitHub
parent fe724ef186
commit 768b05ac1b
15 changed files with 107 additions and 29 deletions

View File

@ -19,3 +19,7 @@ if APP_DIR is None:
if system.IS_WIN and 'bin' not in APP_DIR:
exit('Please use launcher from "bin" folder in "APP_DIR"')
APP_DIR = SystemPath(APP_DIR)
# Application will be stuck after the first test execution if set to False
# We need to investigate more time on it.
ATTACH_MODE = True

View File

@ -1,5 +1,4 @@
import os
LOCAL_RUN = False
ATTACH_MODE = False
APP_DIR = os.getenv('APP_DIR')

View File

@ -1,3 +1,2 @@
LOCAL_RUN = True
ATTACH_MODE = True
APP_DIR = None

View File

@ -6,6 +6,7 @@ import pytest
from PIL import ImageGrab
import configs
import driver
from driver.aut import AUT
from scripts.utils.system_path import SystemPath
from tests.fixtures.path import generate_test_info
@ -58,6 +59,7 @@ def pytest_exception_interact(node):
name='Screenshot on fail',
body=screenshot.read_bytes(),
attachment_type=allure.attachment_type.PNG)
driver.context.detach()
AUT().stop()
except Exception as ex:
_logger.debug(ex)

View File

@ -49,8 +49,11 @@ class AUT:
return self
@allure.step('Close application by process name')
def stop(self, verify: bool = True):
local_system.kill_process_by_name(self.process_name, verify=verify)
def stop(self):
if configs.LOCAL_RUN:
local_system.kill_process_by_port(self.port)
else:
local_system.kill_process_by_name(self.process_name)
@allure.step("Start application")
def launch(self, *args) -> 'AUT':

View File

@ -1,3 +1,5 @@
import logging
import time
import typing
import configs.testpath
@ -5,6 +7,8 @@ from scripts.utils import local_system
_PROCESS_NAME = '_squishserver'
_logger = logging.getLogger(__name__)
class SquishServer:
@ -22,19 +26,27 @@ class SquishServer:
cmd = [
f'"{self.path}"',
'--configfile', str(self.config),
'--verbose',
f'--host={self.host}',
f'--port={self.port}',
]
local_system.execute(cmd)
try:
local_system.wait_for_started(_PROCESS_NAME)
except AssertionError:
except AssertionError as err:
_logger.info(err)
local_system.execute(cmd, check=True)
@classmethod
def stop(cls, attempt: int = 2):
local_system.kill_process_by_name(_PROCESS_NAME, verify=False)
def stop(self, attempt: int = 2):
local_system.run([self.path, '--stop'])
try:
local_system.wait_for_close(_PROCESS_NAME, 2)
except AssertionError as err:
_logger.debug(err)
if attempt:
time.sleep(1)
self.stop(attempt-1)
else:
raise err
# https://doc-snapshots.qt.io/squish/cli-squishserver.html
def configuring(self, action: str, options: typing.Union[int, str, list]):

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -33,6 +33,35 @@ def find_process_by_name(process_name: str):
return processes
@allure.step('Find process by pid')
def find_process_by_pid(pid):
for proc in psutil.process_iter():
try:
if proc.pid == pid:
return process_info(
proc.pid,
proc.name(),
datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f")
)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
@allure.step('Find process by port')
def find_process_by_port(port: int):
for proc in psutil.process_iter():
try:
for conns in proc.connections(kind='inet'):
if conns.laddr.port == port:
return process_info(
proc.pid,
proc.name(),
datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f")
)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
@allure.step('Kill process by name')
def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: int = 10):
_logger.info(f'Closing process: {process_name}')
@ -46,8 +75,22 @@ def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: in
wait_for_close(process_name, timeout_sec)
@allure.step('Kill process by PID')
def kill_process_by_pid(pid, verify: bool = True, timeout_sec: int = 10):
os.kill(pid, signal.SIGILL if IS_WIN else signal.SIGKILL)
if verify:
wait_for_close(pid=pid, timeout_sec=timeout_sec)
@allure.step('Kill process by port')
def kill_process_by_port(port: int):
proc = find_process_by_port(port)
if proc is not None and proc.pid:
kill_process_by_pid(proc.pid)
@allure.step('Wait for process start')
def wait_for_started(process_name: str, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC):
def wait_for_started(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC):
started_at = time.monotonic()
while True:
process = find_process_by_name(process_name)
@ -60,11 +103,19 @@ def wait_for_started(process_name: str, timeout_sec: int = configs.timeouts.PROC
@allure.step('Wait for process close')
def wait_for_close(process_name: str, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC):
def wait_for_close(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC, pid=None):
started_at = time.monotonic()
while True:
if not find_process_by_name(process_name):
break
if process_name is not None:
process = find_process_by_name(process_name)
if not process:
break
elif pid is not None:
process = find_process_by_pid(pid)
if process is None:
break
else:
raise RuntimeError('Set process name or PID to find process')
time.sleep(1)
assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name}'
_logger.info(f'Process closed: {process_name}')
@ -112,3 +163,4 @@ def run(
process = subprocess.run(command, shell=shell, stderr=stderr, stdout=stdout, timeout=timeout_sec)
if check and process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, command, process.stdout, process.stderr)
_logger.debug(f'stdout: {process.stdout}')

View File

@ -1,4 +1,5 @@
import logging
import allure
import pytest
from allure import step
@ -18,13 +19,15 @@ pytestmark = allure.suite("Onboarding")
@allure.testcase('https://ethstatus.testrail.net/index.php?/cases/view/703421', 'Generate new keys')
@pytest.mark.case(703421)
@pytest.mark.parametrize('user_name, password, user_image', [
pytest.param('Test-User _1', '*P@ssw0rd*', None),
pytest.param('_1Test-User', '*P@ssw0rd*', 'tv_signal.jpeg', marks=pytest.mark.smoke),
pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png'),
@pytest.mark.parametrize('user_name, password, user_image, zoom, shift', [
pytest.param('Test-User _1', '*P@ssw0rd*', None, None, None),
pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png', 5, shift_image(0, 0, 0, 0)),
pytest.param('_1Test-User', '*P@ssw0rd*', 'tv_signal.jpeg', 5, shift_image(0, 1000, 1000, 0),
marks=pytest.mark.smoke),
])
def test_generate_new_keys(main_window, user_name, password, user_image: str):
def test_generate_new_keys(main_window, user_name, password, user_image: str, zoom, shift):
with step('Open Generate new keys view'):
if configs.system.IS_MAC:
AllowNotificationsView().wait_until_appears().allow()
BeforeStartedPopUp().get_started()
@ -32,17 +35,17 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
keys_screen = wellcome_screen.get_keys()
with step(f'Setup profile with name: {user_name} and image: {user_image}'):
profile_view = keys_screen.generate_new_keys()
profile_view.set_display_name(user_name)
if user_image is not None:
profile_picture_popup = profile_view.set_user_image(configs.testpath.TEST_FILES / user_image)
profile_picture_popup.make_profile_picture(zoom=5, shift=shift_image(0, 200, 200, 0))
profile_picture_popup.make_profile_picture(zoom=zoom, shift=shift)
assert not profile_view.error_message
with step('Open Profile details view'):
details_view = profile_view.next()
with step('Open Profile details view and verify user info'):
with step('Verify Profile details'):
details_view = profile_view.next()
if user_image is None:
assert not details_view.is_user_image_background_white()
assert driver.waitFor(
@ -52,14 +55,15 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
else:
image.compare(
details_view.cropped_profile_image,
configs.testpath.TEST_VP / f'user_image_onboarding.png',
f'{user_image.split(".")[1]}_onboarding.png',
threshold=0.9
)
chat_key = details_view.chat_key
emoji_hash = details_view.emoji_hash
assert details_view.is_identicon_ring_visible
with step('Finalize onboarding and prepare main screen'):
with step('Finalize onboarding and open main screen'):
create_password_view = details_view.next()
assert not create_password_view.is_create_password_button_enabled
confirm_password_view = create_password_view.create_password(password)
@ -69,7 +73,8 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
SplashScreen().wait_until_appears().wait_until_hidden()
WelcomeStatusPopup().confirm()
with step('Open User Canvas and verify profile'):
with step('Open User Canvas and verify user info'):
user_canvas = main_window.left_panel.open_user_canvas()
assert user_canvas.user_name == user_name
if user_image is None:
@ -78,11 +83,12 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
configs.timeouts.UI_LOAD_TIMEOUT_MSEC
)
with step('Open Profile popup and verify profile'):
with step('Open Profile popup and verify user info'):
profile_popup = user_canvas.open_profile_popup()
assert profile_popup.user_name == user_name
assert profile_popup.chat_key == chat_key
assert profile_popup.emoji_hash.compare(emoji_hash.view)
assert profile_popup.emoji_hash.compare(emoji_hash.view, threshold=0.9)
if user_image is None:
assert driver.waitFor(
lambda: profile_popup.is_user_image_contains(user_name[:2]),
@ -91,5 +97,6 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
else:
image.compare(
profile_popup.cropped_profile_image,
'user_image_profile.png',
f'{user_image.split(".")[1]}_profile.png',
threshold=0.9
)