Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎨 Improving E2E tests: expected_service_running #6739

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = datetime.now(tz=UTC) # noqa: RUF009
matusdrobuliak66 marked this conversation as resolved.
Show resolved Hide resolved

def __call__(self, message: str) -> bool:
# socket.io encodes messages like so
Expand Down Expand Up @@ -234,6 +239,18 @@ 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(
f"Querying the service endpoint from the E2E test. Response: {response}"
)
if response.status_code == 200:
matusdrobuliak66 marked this conversation as resolved.
Show resolved Hide resolved
return True
matusdrobuliak66 marked this conversation as resolved.
Show resolved Hide resolved
self._last_poll_timestamp = datetime.now(UTC)

return False

def got_expected_node_progress_types(self):
Expand All @@ -245,6 +262,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 +352,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 +389,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
Loading