Skip to content

Commit 1579eda

Browse files
committed
added fixture to write into test-results folder
1 parent aa7397a commit 1579eda

File tree

7 files changed

+78
-13
lines changed

7 files changed

+78
-13
lines changed

packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py

+53-13
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,25 @@
88
from dataclasses import dataclass, field
99
from datetime import UTC, datetime, timedelta
1010
from enum import Enum, unique
11+
from pathlib import Path
1112
from typing import Any, Final
1213

1314
import pytest
1415
from playwright._impl._sync_base import EventContextManager
15-
from playwright.sync_api import APIRequestContext, FrameLocator, Locator, Page, Request
16+
from playwright.sync_api import (
17+
APIRequestContext,
18+
)
19+
from playwright.sync_api import Error as PlaywrightError
20+
from playwright.sync_api import (
21+
FrameLocator,
22+
Locator,
23+
Page,
24+
Request,
25+
)
1626
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
17-
from playwright.sync_api import WebSocket
27+
from playwright.sync_api import (
28+
WebSocket,
29+
)
1830
from pydantic import AnyUrl
1931

2032
from .logging_tools import log_context
@@ -112,6 +124,9 @@ class SocketIOEvent:
112124
name: str
113125
obj: dict[str, Any]
114126

127+
def to_json(self) -> str:
128+
return json.dumps({"name": self.name, "obj": self.obj})
129+
115130

116131
SOCKETIO_MESSAGE_PREFIX: Final[str] = "42"
117132

@@ -325,20 +340,20 @@ class SocketIONodeProgressCompleteWaiter:
325340
product_url: AnyUrl
326341
api_request_context: APIRequestContext
327342
is_service_legacy: bool
343+
assertion_output_folder: Path
328344
_current_progress: dict[NodeProgressType, float] = field(
329345
default_factory=defaultdict
330346
)
331347
_last_poll_timestamp: datetime = field(default_factory=lambda: datetime.now(tz=UTC))
332-
_number_of_messages_received: int = 0
348+
_received_messages: list[SocketIOEvent] = field(default_factory=list)
333349
_service_ready: bool = False
334350

335351
def __call__(self, message: str) -> bool:
336352
# socket.io encodes messages like so
337353
# https://stackoverflow.com/questions/24564877/what-do-these-numbers-mean-in-socket-io-payload
338354
if message.startswith(SOCKETIO_MESSAGE_PREFIX):
339-
self._number_of_messages_received += 1
340355
decoded_message = decode_socketio_42_message(message)
341-
self.logger.info("Received message: %s", decoded_message.name)
356+
self._received_messages.append(decoded_message)
342357
if (
343358
(decoded_message.name == _OSparcMessages.SERVICE_STATUS.value)
344359
and (decoded_message.obj["service_uuid"] == self.node_id)
@@ -392,8 +407,12 @@ def __call__(self, message: str) -> bool:
392407
url = (
393408
f"https://{self.node_id}.services.{self.get_partial_product_url()}"
394409
)
395-
with contextlib.suppress(PlaywrightTimeoutError, TimeoutError):
410+
response = None
411+
with contextlib.suppress(
412+
PlaywrightTimeoutError, TimeoutError, PlaywrightError
413+
):
396414
response = self.api_request_context.get(url, timeout=5000)
415+
if response:
397416
self.logger.log(
398417
(
399418
logging.ERROR
@@ -418,11 +437,11 @@ def __call__(self, message: str) -> bool:
418437
)
419438
self._service_ready = True
420439
return True
421-
self._last_poll_timestamp = datetime.now(UTC)
440+
self._last_poll_timestamp = datetime.now(UTC)
422441

423442
return False
424443

425-
def got_expected_node_progress_types(self):
444+
def got_expected_node_progress_types(self) -> bool:
426445
return all(
427446
progress_type in self._current_progress
428447
for progress_type in NodeProgressType.required_types_for_started_service()
@@ -431,12 +450,28 @@ def got_expected_node_progress_types(self):
431450
def get_current_progress(self):
432451
return self._current_progress.values()
433452

434-
def get_partial_product_url(self):
453+
def get_partial_product_url(self) -> str:
435454
return f"{self.product_url}".split("//")[1]
436455

437456
@property
438-
def is_service_ready(self) -> bool:
439-
return self._service_ready
457+
def number_received_messages(self) -> int:
458+
return len(self._received_messages)
459+
460+
def assert_service_ready(self) -> None:
461+
if not self._service_ready:
462+
with self.assertion_output_folder.joinpath("websocket.json").open("w") as f:
463+
f.writelines("[")
464+
f.writelines(
465+
f"{msg.to_json()}," for msg in self._received_messages[:-1]
466+
)
467+
f.writelines(
468+
f"{self._received_messages[-1].to_json()}"
469+
) # no comma for last element
470+
f.writelines("]")
471+
assert self._service_ready, (
472+
f"the service failed and received {self.number_received_messages} websocket messages while waiting!"
473+
"\nTIP: check websocket.log for detailed information in the test-results folder!"
474+
)
440475

441476

442477
_FAIL_FAST_COMPUTATIONAL_STATES: Final[tuple[RunningState, ...]] = (
@@ -510,6 +545,7 @@ def expected_service_running(
510545
press_start_button: bool,
511546
product_url: AnyUrl,
512547
is_service_legacy: bool,
548+
assertion_output_folder: Path,
513549
) -> Generator[ServiceRunning, None, None]:
514550
with log_context(
515551
logging.INFO, msg=f"Waiting for node to run. Timeout: {timeout}"
@@ -520,6 +556,7 @@ def expected_service_running(
520556
product_url=product_url,
521557
api_request_context=page.request,
522558
is_service_legacy=is_service_legacy,
559+
assertion_output_folder=assertion_output_folder,
523560
)
524561
service_running = ServiceRunning(iframe_locator=None)
525562

@@ -528,7 +565,7 @@ def expected_service_running(
528565
_trigger_service_start(page, node_id)
529566

530567
yield service_running
531-
assert waiter.is_service_ready
568+
waiter.assert_service_ready()
532569
service_running.iframe_locator = page.frame_locator(
533570
f'[osparc-test-id="iframe_{node_id}"]'
534571
)
@@ -543,6 +580,7 @@ def wait_for_service_running(
543580
press_start_button: bool,
544581
product_url: AnyUrl,
545582
is_service_legacy: bool,
583+
assertion_output_folder: Path,
546584
) -> FrameLocator:
547585
"""NOTE: if the service was already started this will not work as some of the required websocket events will not be emitted again
548586
In which case this will need further adjutment"""
@@ -556,11 +594,13 @@ def wait_for_service_running(
556594
product_url=product_url,
557595
api_request_context=page.request,
558596
is_service_legacy=is_service_legacy,
597+
assertion_output_folder=assertion_output_folder,
559598
)
560599
with websocket.expect_event("framereceived", waiter, timeout=timeout):
561600
if press_start_button:
562601
_trigger_service_start(page, node_id)
563-
assert waiter.is_service_ready
602+
603+
waiter.assert_service_ready()
564604
return page.frame_locator(f'[osparc-test-id="iframe_{node_id}"]')
565605

566606

packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import re
44
from dataclasses import dataclass
5+
from pathlib import Path
56
from typing import Final, TypedDict
67

78
import arrow
@@ -107,6 +108,7 @@ def wait_for_launched_s4l(
107108
copy_workspace: bool,
108109
product_url: AnyUrl,
109110
is_service_legacy: bool,
111+
assertion_output_folder: Path,
110112
) -> WaitForS4LDict:
111113
with log_context(logging.INFO, "launch S4L") as ctx:
112114
predicate = S4LWaitForWebsocket(logger=ctx.logger)
@@ -134,6 +136,7 @@ def wait_for_launched_s4l(
134136
press_start_button=False,
135137
product_url=product_url,
136138
is_service_legacy=is_service_legacy,
139+
assertion_output_folder=assertion_output_folder,
137140
)
138141
s4l_websocket = ws_info.value
139142
ctx.logger.info("acquired S4L websocket!")

tests/e2e-playwright/tests/conftest.py

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import urllib.parse
1515
from collections.abc import Callable, Iterator
1616
from contextlib import ExitStack
17+
from pathlib import Path
1718
from typing import Any, Final
1819

1920
import arrow
@@ -794,3 +795,10 @@ def _do() -> SocketIOEvent:
794795
logging.INFO, f"Stop computation with {pipeline_id=} in {product_url=}"
795796
):
796797
api_request_context.post(f"{product_url}v0/computations/{pipeline_id}:stop")
798+
799+
800+
@pytest.fixture
801+
def playwright_test_results_dir() -> Path:
802+
results_dir = Path.cwd() / "test-results"
803+
results_dir.mkdir(parents=True, exist_ok=True)
804+
return results_dir

tests/e2e-playwright/tests/jupyterlabs/test_jupyterlab.py

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import re
1212
from collections.abc import Callable
1313
from dataclasses import dataclass
14+
from pathlib import Path
1415
from typing import Any, Final, Literal
1516

1617
from playwright.sync_api import Page, WebSocket
@@ -72,6 +73,7 @@ def test_jupyterlab(
7273
large_file_block_size: ByteSize,
7374
product_url: AnyUrl,
7475
is_service_legacy: bool,
76+
playwright_test_results_dir: Path,
7577
):
7678
# NOTE: this waits for the jupyter to send message, but is not quite enough
7779
with (
@@ -104,6 +106,7 @@ def test_jupyterlab(
104106
press_start_button=False,
105107
product_url=product_url,
106108
is_service_legacy=is_service_legacy,
109+
assertion_output_folder=playwright_test_results_dir,
107110
)
108111

109112
iframe = page.frame_locator("iframe")

tests/e2e-playwright/tests/sim4life/test_sim4life.py

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
from collections.abc import Callable
11+
from pathlib import Path
1112
from typing import Any
1213

1314
from playwright.sync_api import Page
@@ -34,6 +35,7 @@ def test_sim4life(
3435
check_videostreaming: bool,
3536
product_url: AnyUrl,
3637
is_service_legacy: bool,
38+
playwright_test_results_dir: Path,
3739
):
3840
if use_plus_button:
3941
project_data = create_project_from_new_button(service_key)
@@ -57,6 +59,7 @@ def test_sim4life(
5759
copy_workspace=False,
5860
product_url=product_url,
5961
is_service_legacy=is_service_legacy,
62+
assertion_output_folder=playwright_test_results_dir,
6063
)
6164
s4l_websocket = resp["websocket"]
6265
s4l_iframe = resp["iframe"]

tests/e2e-playwright/tests/sim4life/test_template.py

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
from collections.abc import Callable
11+
from pathlib import Path
1112
from typing import Any
1213

1314
from playwright.sync_api import Page
@@ -29,6 +30,7 @@ def test_template(
2930
check_videostreaming: bool,
3031
product_url: AnyUrl,
3132
is_service_legacy: bool,
33+
playwright_test_results_dir: Path,
3234
):
3335
project_data = create_project_from_template_dashboard(template_id)
3436

@@ -47,6 +49,7 @@ def test_template(
4749
copy_workspace=True,
4850
product_url=product_url,
4951
is_service_legacy=is_service_legacy,
52+
assertion_output_folder=playwright_test_results_dir,
5053
)
5154
s4l_websocket = resp["websocket"]
5255
s4l_iframe = resp["iframe"]

tests/e2e-playwright/tests/tip/test_ti_plan.py

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import re
1212
from collections.abc import Callable
1313
from dataclasses import dataclass
14+
from pathlib import Path
1415
from typing import Any, Final
1516

1617
from playwright.sync_api import Page, WebSocket
@@ -96,6 +97,7 @@ def test_classic_ti_plan( # noqa: PLR0915
9697
create_tip_plan_from_dashboard: Callable[[str], dict[str, Any]],
9798
product_url: AnyUrl,
9899
is_service_legacy: bool,
100+
playwright_test_results_dir: Path,
99101
):
100102
with log_context(logging.INFO, "Checking 'Access TIP' teaser"):
101103
# click to open and expand
@@ -147,6 +149,7 @@ def test_classic_ti_plan( # noqa: PLR0915
147149
press_start_button=False,
148150
product_url=product_url,
149151
is_service_legacy=is_service_legacy,
152+
assertion_output_folder=playwright_test_results_dir,
150153
)
151154
# NOTE: Sometimes this iframe flicks and shows a white page. This wait will avoid it
152155
page.wait_for_timeout(_ELECTRODE_SELECTOR_FLICKERING_WAIT_TIME)
@@ -212,6 +215,7 @@ def test_classic_ti_plan( # noqa: PLR0915
212215
press_start_button=False,
213216
product_url=product_url,
214217
is_service_legacy=is_service_legacy,
218+
assertion_output_folder=playwright_test_results_dir,
215219
) as service_running:
216220
app_mode_trigger_next_app(page)
217221
ti_iframe = service_running.iframe_locator
@@ -326,6 +330,7 @@ def test_classic_ti_plan( # noqa: PLR0915
326330
press_start_button=False,
327331
product_url=product_url,
328332
is_service_legacy=is_service_legacy,
333+
assertion_output_folder=playwright_test_results_dir,
329334
) as service_running:
330335
app_mode_trigger_next_app(page)
331336
s4l_postpro_iframe = service_running.iframe_locator

0 commit comments

Comments
 (0)