Skip to content

Commit

Permalink
🎨 Improving E2E tests: expected_service_running (ITISFoundation#6739)
Browse files Browse the repository at this point in the history
  • Loading branch information
matusdrobuliak66 authored Nov 18, 2024
1 parent 7ec1d25 commit c83d60c
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 17 deletions.
39 changes: 37 additions & 2 deletions packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
from collections import defaultdict
from collections.abc import Generator, Iterator
from dataclasses import dataclass, field
from datetime import UTC, datetime, timedelta
from enum import Enum, unique
from typing import Any, Final

import httpx
from playwright.sync_api import FrameLocator, Page, Request
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
from playwright.sync_api import WebSocket
from pydantic import AnyUrl
from pytest_simcore.helpers.logging_tools import log_context

SECOND: Final[int] = 1000
Expand Down Expand Up @@ -196,9 +199,11 @@ def __call__(self, message: str) -> None:
class SocketIONodeProgressCompleteWaiter:
node_id: str
logger: logging.Logger
product_url: AnyUrl
_current_progress: dict[NodeProgressType, float] = field(
default_factory=defaultdict
)
_last_poll_timestamp: datetime = field(default_factory=lambda: datetime.now(tz=UTC))

def __call__(self, message: str) -> bool:
# socket.io encodes messages like so
Expand Down Expand Up @@ -234,6 +239,27 @@ def __call__(self, message: str) -> bool:
round(progress, 1) == 1.0
for progress in self._current_progress.values()
)

_current_timestamp = datetime.now(UTC)
if _current_timestamp - self._last_poll_timestamp > timedelta(seconds=5):
url = f"https://{self.node_id}.services.{self.get_partial_product_url()}"
response = httpx.get(url, timeout=10)
self.logger.info(
"Querying the service endpoint from the E2E test. Url: %s Response: %s",
url,
response,
)
if response.status_code <= 401:
# NOTE: If the response status is less than 400, it means that the backend is ready (There are some services that respond with a 3XX)
# MD: for now I have included 401 - as this also means that backend is ready
if self.got_expected_node_progress_types():
self.logger.warning(
"⚠️ Progress bar didn't receive 100 percent but service is already running: %s ⚠️", # https://github.com/ITISFoundation/osparc-simcore/issues/6449
self.get_current_progress(),
)
return True
self._last_poll_timestamp = datetime.now(UTC)

return False

def got_expected_node_progress_types(self):
Expand All @@ -245,6 +271,9 @@ def got_expected_node_progress_types(self):
def get_current_progress(self):
return self._current_progress.values()

def get_partial_product_url(self):
return f"{self.product_url}".split("//")[1]


def wait_for_pipeline_state(
current_state: RunningState,
Expand Down Expand Up @@ -332,9 +361,12 @@ def expected_service_running(
websocket: WebSocket,
timeout: int,
press_start_button: bool,
product_url: AnyUrl,
) -> Generator[ServiceRunning, None, None]:
with log_context(logging.INFO, msg="Waiting for node to run") as ctx:
waiter = SocketIONodeProgressCompleteWaiter(node_id=node_id, logger=ctx.logger)
waiter = SocketIONodeProgressCompleteWaiter(
node_id=node_id, logger=ctx.logger, product_url=product_url
)
service_running = ServiceRunning(iframe_locator=None)

try:
Expand Down Expand Up @@ -366,12 +398,15 @@ def wait_for_service_running(
websocket: WebSocket,
timeout: int,
press_start_button: bool,
product_url: AnyUrl,
) -> FrameLocator:
"""NOTE: if the service was already started this will not work as some of the required websocket events will not be emitted again
In which case this will need further adjutment"""

with log_context(logging.INFO, msg="Waiting for node to run") as ctx:
waiter = SocketIONodeProgressCompleteWaiter(node_id=node_id, logger=ctx.logger)
waiter = SocketIONodeProgressCompleteWaiter(
node_id=node_id, logger=ctx.logger, product_url=product_url
)
with websocket.expect_event("framereceived", waiter, timeout=timeout):
if press_start_button:
_trigger_service_start(page, node_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

import arrow
from playwright.sync_api import FrameLocator, Page, WebSocket, expect
from pydantic import TypeAdapter # pylint: disable=no-name-in-module
from pydantic import ByteSize
from pydantic import AnyUrl, ByteSize, TypeAdapter # pylint: disable=no-name-in-module

from .logging_tools import log_context
from .playwright import (
Expand Down Expand Up @@ -105,6 +104,7 @@ def wait_for_launched_s4l(
*,
autoscaled: bool,
copy_workspace: bool,
product_url: AnyUrl,
) -> WaitForS4LDict:
with log_context(logging.INFO, "launch S4L") as ctx:
predicate = S4LWaitForWebsocket(logger=ctx.logger)
Expand All @@ -130,6 +130,7 @@ def wait_for_launched_s4l(
)
+ (_S4L_COPY_WORKSPACE_TIME if copy_workspace else 0),
press_start_button=False,
product_url=product_url,
)
s4l_websocket = ws_info.value
ctx.logger.info("acquired S4L websocket!")
Expand Down
1 change: 1 addition & 0 deletions tests/e2e-playwright/requirements/_test.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ pytest-runner
pytest-sugar
pyyaml
tenacity
httpx
49 changes: 36 additions & 13 deletions tests/e2e-playwright/requirements/_test.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile _test.in
#
annotated-types==0.7.0
# via pydantic
anyio==4.6.2.post1
# via httpx
arrow==1.3.0
# via -r requirements/_test.in
# via -r _test.in
certifi==2024.8.30
# via requests
# via
# httpcore
# httpx
# requests
charset-normalizer==3.3.2
# via requests
dnspython==2.6.1
# via email-validator
docker==7.1.0
# via -r requirements/_test.in
# via -r _test.in
email-validator==2.2.0
# via pydantic
faker==29.0.0
# via -r requirements/_test.in
# via -r _test.in
greenlet==3.0.3
# via playwright
h11==0.14.0
# via httpcore
httpcore==1.0.7
# via httpx
httpx==0.27.2
# via -r _test.in
idna==3.10
# via
# anyio
# email-validator
# httpx
# requests
iniconfig==2.0.0
# via pytest
Expand All @@ -34,8 +53,8 @@ playwright==1.47.0
# via pytest-playwright
pluggy==1.5.0
# via pytest
pydantic==2.9.2
# via -r requirements/_test.in
pydantic[email]==2.9.2
# via -r _test.in
pydantic-core==2.23.4
# via pydantic
pyee==12.0.0
Expand All @@ -51,33 +70,37 @@ pytest==8.3.3
pytest-base-url==2.1.0
# via pytest-playwright
pytest-html==4.1.1
# via -r requirements/_test.in
# via -r _test.in
pytest-instafail==0.5.0
# via -r requirements/_test.in
# via -r _test.in
pytest-metadata==3.1.1
# via pytest-html
pytest-playwright==0.5.2
# via -r requirements/_test.in
# via -r _test.in
pytest-runner==6.0.1
# via -r requirements/_test.in
# via -r _test.in
pytest-sugar==1.0.0
# via -r requirements/_test.in
# via -r _test.in
python-dateutil==2.9.0.post0
# via
# arrow
# faker
python-slugify==8.0.4
# via pytest-playwright
pyyaml==6.0.2
# via -r requirements/_test.in
# via -r _test.in
requests==2.32.3
# via
# docker
# pytest-base-url
six==1.16.0
# via python-dateutil
sniffio==1.3.1
# via
# anyio
# httpx
tenacity==9.0.0
# via -r requirements/_test.in
# via -r _test.in
termcolor==2.4.0
# via pytest-sugar
text-unidecode==1.3
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e-playwright/tests/sim4life/test_sim4life.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Any

from playwright.sync_api import Page, WebSocket
from pydantic import AnyUrl
from pytest_simcore.helpers.playwright import (
ServiceType,
web_socket_default_log_handler,
Expand All @@ -33,6 +34,7 @@ def test_sim4life(
use_plus_button: bool,
is_autoscaled: bool,
check_videostreaming: bool,
product_url: AnyUrl,
):
if use_plus_button:
project_data = create_project_from_new_button(service_key)
Expand All @@ -54,6 +56,7 @@ def test_sim4life(
log_in_and_out,
autoscaled=is_autoscaled,
copy_workspace=False,
product_url=product_url,
)
s4l_websocket = resp["websocket"]
with web_socket_default_log_handler(s4l_websocket):
Expand Down
5 changes: 5 additions & 0 deletions tests/e2e-playwright/tests/tip/test_ti_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from typing import Any, Final

from playwright.sync_api import Page, WebSocket
from pydantic import AnyUrl
from pytest_simcore.helpers.logging_tools import log_context
from pytest_simcore.helpers.playwright import (
MINUTE,
Expand Down Expand Up @@ -92,6 +93,7 @@ def test_classic_ti_plan( # noqa: PLR0915
is_autoscaled: bool,
is_product_lite: bool,
create_tip_plan_from_dashboard: Callable[[str], dict[str, Any]],
product_url: AnyUrl,
):
with log_context(logging.INFO, "Checking 'Access TIP' teaser"):
# click to open and expand
Expand Down Expand Up @@ -141,6 +143,7 @@ def test_classic_ti_plan( # noqa: PLR0915
else _ELECTRODE_SELECTOR_MAX_STARTUP_TIME
),
press_start_button=False,
product_url=product_url,
)
# NOTE: Sometimes this iframe flicks and shows a white page. This wait will avoid it
page.wait_for_timeout(_ELECTRODE_SELECTOR_FLICKERING_WAIT_TIME)
Expand Down Expand Up @@ -200,6 +203,7 @@ def test_classic_ti_plan( # noqa: PLR0915
else _JLAB_MAX_STARTUP_MAX_TIME
),
press_start_button=False,
product_url=product_url,
) as service_running:
app_mode_trigger_next_app(page)
ti_iframe = service_running.iframe_locator
Expand Down Expand Up @@ -284,6 +288,7 @@ def test_classic_ti_plan( # noqa: PLR0915
else _POST_PRO_MAX_STARTUP_TIME
),
press_start_button=False,
product_url=product_url,
) as service_running:
app_mode_trigger_next_app(page)
s4l_postpro_iframe = service_running.iframe_locator
Expand Down

0 comments on commit c83d60c

Please sign in to comment.